From cbe9c1dabcb9c35e27b7bd4f34ad056c6a908c4e Mon Sep 17 00:00:00 2001 From: xialixin Date: Fri, 20 Mar 2020 00:01:48 +0800 Subject: [PATCH 01/13] Support gb28181 sip over udp, ps steam over rtp --- trunk/conf/push.gb28181.conf | 71 ++ trunk/configure | 4 +- trunk/src/app/srs_app_config.cpp | 174 +++- trunk/src/app/srs_app_config.hpp | 12 + trunk/src/app/srs_app_gb28181.cpp | 1438 ++++++++++++++++++++++++++ trunk/src/app/srs_app_gb28181.hpp | 330 ++++++ trunk/src/app/srs_app_listener.cpp | 10 +- trunk/src/app/srs_app_listener.hpp | 2 + trunk/src/app/srs_app_server.cpp | 23 +- trunk/src/app/srs_app_server.hpp | 10 + trunk/src/protocol/srs_sip_stack.cpp | 553 ++++++++++ trunk/src/protocol/srs_sip_stack.hpp | 143 +++ trunk/src/service/srs_service_st.cpp | 5 + trunk/src/service/srs_service_st.hpp | 1 + 14 files changed, 2771 insertions(+), 5 deletions(-) create mode 100644 trunk/conf/push.gb28181.conf create mode 100644 trunk/src/app/srs_app_gb28181.cpp create mode 100644 trunk/src/app/srs_app_gb28181.hpp create mode 100644 trunk/src/protocol/srs_sip_stack.cpp create mode 100644 trunk/src/protocol/srs_sip_stack.hpp diff --git a/trunk/conf/push.gb28181.conf b/trunk/conf/push.gb28181.conf new file mode 100644 index 000000000..b76887d98 --- /dev/null +++ b/trunk/conf/push.gb28181.conf @@ -0,0 +1,71 @@ +# push gb28281 stream to SRS. + +listen 1935; +max_connections 1000; +daemon off; +pid ./objs/srs28181.pid +srs_log_tank console; +stream_caster { + enabled on; + caster gb28181; + + #rtmp输出地址,可以参数化 + #[stream] 代表客户端sip设备编号 + #[timestamp] 时间戳 + output rtmp://127.0.0.1/live/[stream]; + #sip监听udp端口 + listen 15060; + + #服务器主机号,可以域名或ip地址 + #也就是设备端将媒体发送的地址,如果是服务器是内外网 + #需要写外网地址 + host 192.168.1.27; + + #服务器端编号 + #设备端配置编号需要与该值一致,否则无法注册 + serial 34020000002020000001; + + #服务器端域 + realm 3402000000; + + #是否转发音频流 + #目前只支持aac格式,所以需要设备支持aac格式 + #on:转发音频 + #off:不转发音频,只有视频 + #*注意*!!!:flv 只支持11025 22050 44100 三种 + #如果设备端没有三种中任何一个,转发时为自动选择一种格式 + #同时也会将adts的头封装在flv aac raw数据中 + #这样的话播放器为自动通过adts头自动选择采样频率 + #像ffplay, vlc都可以,但是flash是没有声音, + #因为flash,只支持11025 22050 44100 + audio_enable on; + + #服务端发送ack后,接收回应的超时时间,单位为秒 + #如果指定时间没有回应,认为失败 + ack_timeout 30; + + #设备心跳维持时间,如果指定时间内(秒)没有接收一个心跳 + #认为设备离线 + keepalive_timeout 30; + + #是否等待关键帧之后,再转发, + #off:不需等待,直接转发 + #on:等第一个关键帧后,再转发 + wait_keyframe off; + + #日志打印是否打印sip信息 + #off:不打印 + #on:打印接收或发送sip命令信息 + print_sip_message off; + + #rtp包空闲等待时间,如果指定时间没有收到任何包 + #rtp监听连接自动停止,发送BYE命令 + rtp_idle_timeout 30; + + #rtp接收监听端口范围,最小值 + rtp_port_min 58200; + #rtp接收监听端口范围,最大值 + rtp_port_max 58300; +} +vhost __defaultVhost__ { +} diff --git a/trunk/configure b/trunk/configure index d233f66db..0b50507e7 100755 --- a/trunk/configure +++ b/trunk/configure @@ -213,7 +213,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") PROTOCOL_INCS="src/protocol"; MODULE_DIR=${PROTOCOL_INCS} . auto/modules.sh PROTOCOL_OBJS="${MODULE_OBJS[@]}" @@ -257,7 +257,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") DEFINES="" # add each modules for app for SRS_MODULE in ${SRS_MODULES[*]}; do diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 7a9f86a0a..bb4fc793a 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,26 @@ 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()); } + } obj->set(dir->name, sobj); } else { @@ -3647,7 +3671,11 @@ 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 != "ack_timeout" && n != "keepalive_timeout" + && n != "host" && n != "serial" && n != "realm" + && n != "audio_enable" && n != "wait_keyframe" + && n != "print_sip_message") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", n.c_str()); } } @@ -4179,6 +4207,150 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf) return ::atoi(conf->arg0().c_str()); } +int SrsConfig::get_stream_caster_gb28181_rtp_ide_timeout(SrsConfDirective* conf) +{ + static int DEFAULT = 30; + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("rtp_ide_timeout"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return ::atoi(conf->arg0().c_str()); +} + +int SrsConfig::get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf) +{ + static int DEFAULT = 30; + + 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 = 30; + + 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 = "127.0.0.1"; + + 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 = ""; + + 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 = ""; + + 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 = true; + + 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("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()); +} + SrsConfDirective* SrsConfig::get_vhost(string vhost, bool try_default_vhost) { srs_assert(root); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 90a57e0c3..ffae3baff 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); @@ -484,6 +485,17 @@ public: virtual int get_stream_caster_rtp_port_min(SrsConfDirective* conf); // Get the max udp port for rtp of stream caster rtsp. virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf); + + virtual int get_stream_caster_gb28181_rtp_ide_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); + // vhost specified section public: // Get the vhost directive by vhost name. diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp new file mode 100644 index 000000000..b4d647ce1 --- /dev/null +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -0,0 +1,1438 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * 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 atleast 4bytes content. + if (!stream->require(4)) { + return srs_error_new(ERROR_RTP_TYPE96_CORRUPT, "requires 4 only %d bytes", stream->left()); + } + + // append left bytes to payload. + payload->append(stream->data() + stream->pos() , stream->size()-stream->pos()); + + } + return err; +} + + +SrsPsRtpConn::SrsPsRtpConn(SrsGb28181Conn* conn, int p, std::string sid, bool b, bool k) +{ + gb28181 = conn; + _port = p; + session_id = sid; + // TODO: support listen at <[ip:]port> + listener = new SrsUdpListener(this, srs_any_address_for_listener(), p); + cache = new SrsPsRtpPacket(); + pprint = SrsPithyPrint::create_caster(); + pre_timestamp = -1; + + audio_enable = b; + first_keyframe_flag = false; + wait_first_keyframe = k; +} + +SrsPsRtpConn::~SrsPsRtpConn() +{ + ps_fw.close(); + video_fw.close(); + audio_fw.close(); + + dispose(); + + srs_freep(listener); + srs_freep(cache); + srs_freep(pprint); +} + +int SrsPsRtpConn::port() +{ + return _port; +} + +srs_error_t SrsPsRtpConn::listen() +{ + return listener->listen(); +} + +void SrsPsRtpConn::dispose() +{ + map::iterator it; + for (it = cache_payload.begin(); it != cache_payload.end(); ++it) { + srs_freep(it->second); + } + + cache_payload.clear(); + return; +} + +int64_t SrsPsRtpConn::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 SrsPsRtpConn::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +{ + srs_error_t err = srs_success; + bool completed = false; + int keyframe = 0; + + pprint->elapse(); + + if (true) { + SrsBuffer stream(buf, nb_buf); + + SrsPsRtpPacket pkt; + + if ((err = pkt.decode(&stream)) != srs_success) { + srs_trace("decode error"); + return srs_success; + //return srs_error_wrap(err, "decode"); + } + + if (pre_timestamp == -1) { + pre_timestamp = pkt.timestamp; + } + + //cache pkt payload by timestamp + if (cache_payload.find(pkt.timestamp) == cache_payload.end()) { + cache_payload[pkt.timestamp] = new SrsSimpleStream(); + } + + cache_payload[pkt.timestamp]->append(pkt.payload); + + uint32_t cur_timestamp = pkt.timestamp; + + + if (pkt.marker) { + completed = true; + }else if (pre_timestamp != pkt.timestamp){ + if (cache_payload.find(pre_timestamp) != cache_payload.end()) { + completed = true; + cur_timestamp = pre_timestamp; + } + } + + pre_timestamp = pkt.timestamp; + + 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", + session_id.c_str(), nb_buf, pprint->age(), pkt.version, pkt.payload_type, pkt.sequence_number, pkt.timestamp, pkt.ssrc, + pkt.payload->length() + ); + } + + if (!completed){ + return err; + } + + //process completed frame data + char *payload = cache_payload[cur_timestamp]->bytes(); + int payload_len = cache_payload[cur_timestamp]->length(); + + on_ps_stream(payload, payload_len, cur_timestamp); + + //clear processed one ps frame + map::iterator key = cache_payload.find(cur_timestamp); + if(key!=cache_payload.end()) + { + srs_freep(key->second); + cache_payload.erase(key); + } + } + + return err; +} + +bool SrsPsRtpConn::can_send_ps_av_packet(){ + if (!wait_first_keyframe) + return true; + + if (first_keyframe_flag) + return true; + + return false; +} + + +srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp) +{ + 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 keyframe = 0; + int pse_index = 0; + +#ifdef W_PS_FILE + if (!ps_fw.is_open()) { + std::string filename = "test_ps_" + session_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 + + SrsPsePacket* pse_pack = (SrsPsePacket*)next_ps_pack; + + unsigned char pts_dts_flags = (pse_pack->info[0] & 0xF0) >> 6; + int64_t pts = 0; + if (pse_index == 0 && pts_dts_flags > 0) { + pts = parse_ps_timestamp((unsigned char*)next_ps_pack + 9); + //srs_trace("vvvvvvvvvvvvvvvvvvvvvvv ts=%u pkt_ts=%u", pts, timestamp); + } + + if (pse_index == 0) video_pts = pts; + + 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_" + session_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; + + + //srs_trace("====================== V pts=%u", pts); + } + 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_trace("aaaaaaaaaaaaaaaaaaaaaaaaaa 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_" + session_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 (audio_enable && audio_stream.length() && can_send_ps_av_packet()) { + if ((err = gb28181->on_rtp_audio(&audio_stream, audio_pts)) != srs_success) { + srs_trace("process ps audio packet error %s", err); + //return srs_success; + //return srs_error_wrap(err, "process rtp packet"); + } + } + } + else + { + srs_trace("gb28181: client_id %s, unkonw ps data %02x %02x %02x %02x\n", + session_id.c_str(), next_ps_pack[0], next_ps_pack[1], next_ps_pack[2], next_ps_pack[3]); + //srs_trace(" ps ps_size=%d complete=%d h264len=%d\n", ps_size, complete_len, *h264length); + break; + } + } + + if (complete_len != ps_size){ + srs_trace("gb28181: client_id %s decode ps packet error! ps_size=%d complete=%d \n", + session_id.c_str(), ps_size, complete_len); + }else if (video_stream.length() && can_send_ps_av_packet()) { + if ((err = gb28181->on_rtp_video(&video_stream, video_pts, keyframe)) != srs_success) { + srs_trace("process ps video packet error"); + //return srs_success; + return srs_error_wrap(err, "process ps video packet error"); + } + } + + return err; +} + +SrsGb28281ClientInfo::SrsGb28281ClientInfo() +{ + req = new SrsSipRequest(); +} + +SrsGb28281ClientInfo::~SrsGb28281ClientInfo() +{ + srs_freep(req); +} + + +SrsGb28181Conn::SrsGb28181Conn(SrsGb28181Caster* c, std::string id) +{ + session_id = id; + + video_rtp = NULL; + audio_rtp = NULL; + + req = NULL; + caster = c; + info = new SrsGb28281ClientInfo; + pprint = SrsPithyPrint::create_caster(); + + skt = new SrsStSocket(); + sip = new SrsSipStack(); + trd = new SrsSTCoroutine("gb28181conn", this); + + sdk = NULL; + vjitter = new SrsRtspJitter(); + ajitter = new SrsRtspJitter(); + + avc = new SrsRawH264Stream(); + aac = new SrsRawAacStream(); + + h264_sps_changed = false; + h264_pps_changed = false; + h264_sps_pps_sent = false; + + reg_expires = 3600; + register_time = 0; + alive_time = 0; + invite_time = 0; + register_status = Srs28181Unkonw; + alive_status = Srs28181Unkonw; + invite_status = Srs28181Unkonw; +} + +SrsGb28181Conn::~SrsGb28181Conn() +{ + close(); + + srs_freep(info); + + srs_freep(video_rtp); + srs_freep(audio_rtp); + + srs_freep(trd); + srs_freep(skt); + srs_freep(sip); + + srs_freep(sdk); + srs_freep(req); + + srs_freep(vjitter); + srs_freep(ajitter); + + srs_freep(pprint); +} + +srs_error_t SrsGb28181Conn::serve() +{ + srs_error_t err = srs_success; + + if ((err = trd->start()) != srs_success) { + return srs_error_wrap(err, "gb28181 connection"); + } + + return err; +} + +std::string SrsGb28181Conn::remote_ip() +{ + return ""; +} + +void SrsGb28181Conn::set_request_info(SrsSipRequest *req) +{ + srs_assert(req != NULL); + info->req->copy(req); +} + +std::string SrsGb28181Conn::get_session_id() +{ + return session_id; +} + +srs_error_t SrsGb28181Conn::do_cycle() +{ + srs_error_t err = srs_success; + + // consume all sip messages. + while (true) { + + pprint->elapse(); + + if ((err = trd->pull()) != srs_success) { + //srs_trace("pull faild %s")); + //return srs_error_wrap(err, "sip cycle"); + } + + srs_utime_t now = srs_get_system_time(); + SrsGb28181Config config = caster->GetGb28181Config(); + + srs_utime_t reg_duration = 0; + srs_utime_t invite_duration = 0; + srs_utime_t alive_duration = 0; + srs_utime_t recv_rtp_duration = 0; + + if (register_status == Srs28181RegisterOk && register_time > 0) { + reg_duration = (now - register_time) / (1000*1000); + if (reg_duration > reg_expires) { + register_status = Srs28181Unkonw; + alive_status = Srs28181Unkonw; + invite_status = Srs28181Unkonw; + stop_rtp_listen(); + } + } + + if (alive_status == Srs28181AliveOk && alive_time > 0){ + alive_duration = (now - alive_time) / (1000*1000); + if (alive_duration > config.sip_keepalive_timeout) { + srs_trace("gb28181: client id=%s alive timeout, remove conn", session_id.c_str()); + break; + } + } + + if (invite_status && invite_time > 0) { + invite_duration = (now - invite_time) / (1000*1000); + if (invite_status == Srs28181Trying && invite_duration > config.sip_ack_timeout) { + invite_status = Srs28181Unkonw; + stop_rtp_listen(); + } + + recv_rtp_duration = (now - recv_rtp_time) / (1000*1000); + if (recv_rtp_duration > config.rtp_idle_timeout) { + invite_status = Srs28181Unkonw; + stop_rtp_listen(); + } + } + + if (pprint->can_print()) { + srs_trace("gb28181: client id=%s, status, druation reg=%u alive=%u invite=%u", + session_id.c_str(), reg_duration, alive_duration, invite_duration); + } + + srs_usleep(1000 * 1000); + } + + return err; +} + +srs_error_t SrsGb28181Conn::start_rtp_listen(int port) +{ + srs_error_t err = srs_success; + + SrsPsRtpConn* rtp = NULL; + srs_freep(video_rtp); + SrsGb28181Config config = caster->GetGb28181Config(); + rtp = video_rtp = new SrsPsRtpConn(this, port, session_id, + config.audio_enable, config.wait_keyframe); + + if ((err = rtp->listen()) != srs_success) { + return srs_error_wrap(err, "rtp listen"); + } + srs_trace("gb28181conn: start rtp ps stream over server-port=%d", port); + + return err; + +} + +srs_error_t SrsGb28181Conn::stop_rtp_listen() +{ + srs_error_t err = srs_success; + + if (video_rtp) { + caster->free_port(video_rtp->port(), video_rtp->port() + 1); + srs_freep(video_rtp); + } + + if (audio_rtp) { + caster->free_port(audio_rtp->port(), audio_rtp->port() + 1); + srs_freep(audio_rtp); + } + + //stop rtmp publish + close(); + + return err; +} + + +srs_error_t SrsGb28181Conn::cycle() +{ + // serve the sip client. + srs_error_t err = do_cycle(); + + stop_rtp_listen(); + + caster->remove(this); + srs_trace("gb28181conn: client id=%d conn is remove", session_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 SrsGb28181Conn::on_rtp_video(SrsSimpleStream *stream, int64_t fpts, int keyframe) +{ + srs_error_t err = srs_success; + + // ensure rtmp connected. + if ((err = connect()) != srs_success) { + 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); + + recv_rtp_time = srs_get_system_time(); + + //srs_trace("==========================================VVV pts=%u", dts); + 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. + // TODO: FIXME: we should group all frames to a rtmp/flv message from one ts message. + 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 SrsGb28181Conn::on_rtp_audio(SrsSimpleStream* stream, int64_t fdts) +{ + srs_error_t err = srs_success; + + // ensure rtmp connected. + if ((err = connect()) != srs_success) { + return srs_error_wrap(err, "connect"); + } + + // sip tbn is ts tbn. + if ((err = ajitter->correct(fdts)) != srs_success) { + return srs_error_wrap(err, "jitter"); + } + + recv_rtp_time = srs_get_system_time(); + + // ts tbn to flv tbn. + 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 SrsGb28181Conn::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 SrsGb28181Conn::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 SrsGb28181Conn::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 SrsGb28181Conn::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 SrsGb28181Conn::connect() +{ + srs_error_t err = srs_success; + + // Ignore when connected. + if (sdk) { + return err; + } + + // generate rtmp url to connect to. + std::string url; + if (true) { + std::string schema, host, vhost, app, param; + int port; + //srs_discovery_tc_url(rtmp_url, schema, host, vhost, app, rtsp_stream, port, param); + + // generate output by template. + std::string output = rtmp_url; + output = srs_string_replace(output, "[app]", "live"); + output = srs_string_replace(output, "[stream]", session_id); + output = srs_path_build_timestamp(output); + url = output; + } + // 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 SrsGb28181Conn::close() +{ + srs_freep(sdk); +} + +SrsGb28181Config::SrsGb28181Config(SrsConfDirective* c) +{ + // TODO: FIXME: support reload. + output = _srs_config->get_stream_caster_output(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_ide_timeout(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); + listen_port = _srs_config->get_stream_caster_listen(c); + sip_host = _srs_config->get_stream_caster_gb28181_host(c); + sip_realm = _srs_config->get_stream_caster_gb28181_realm(c); + sip_serial = _srs_config->get_stream_caster_gb28181_serial(c); + audio_enable = _srs_config->get_stream_caster_gb28181_audio_enable(c); + print_sip_message = _srs_config->get_stream_caster_gb28181_print_sip_message(c); + wait_keyframe = _srs_config->get_stream_caster_gb28181_wait_keyframe(c); +} + +SrsGb28181Config::~SrsGb28181Config() +{ + +} + +//gb28181 caster +SrsGb28181Caster::SrsGb28181Caster(SrsConfDirective* c) +{ + // TODO: FIXME: support reload. + //output = _srs_config->get_stream_caster_output(c); + //local_port_min = _srs_config->get_stream_caster_rtp_port_min(c); + //local_port_max = _srs_config->get_stream_caster_rtp_port_max(c); + sip = new SrsSipStack(); + manager = new SrsCoroutineManager(); + config = new SrsGb28181Config(c); + lfd = NULL; +} + +SrsGb28181Caster::~SrsGb28181Caster() +{ + used_ports.clear(); + + srs_freep(manager); + srs_freep(sip); + srs_freep(config); + + destroy(); +} + +srs_error_t SrsGb28181Caster::initialize() +{ + srs_error_t err = srs_success; + if ((err = manager->start()) != srs_success) { + return srs_error_wrap(err, "start manager"); + } + + return err; +} + +void SrsGb28181Caster::set_stfd(srs_netfd_t fd) +{ + lfd = fd; +} + +SrsGb28181Config SrsGb28181Caster::GetGb28181Config() +{ + return *config; +} + +srs_error_t SrsGb28181Caster::alloc_port(int* pport) +{ + srs_error_t err = srs_success; + + // 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); + + return err; +} + +void SrsGb28181Caster::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); +} + +srs_error_t SrsGb28181Caster::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); + + // append to buffer. + //buffer->append(buf, nb_buf); + srs_error_t err = on_udp_bytes(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 SrsGb28181Caster::on_udp_bytes(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, "recv message"); + } + + 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); + + 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 srs_success; + } + + 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()); + + SrsGb28181Conn* conn = NULL; + + if ((err = fetch_or_create(req, &conn)) != srs_success) { + srs_trace("gb28181: conn create faild:%s", req->uri.c_str()); + return srs_error_wrap(err, "conn create faild");; + } + srs_assert(conn != NULL); + + if (conn->register_status == Srs28181Unkonw) + { + conn->serve(); + }else{ + srs_trace("gb28181: %s client is register", req->sip_auth_id.c_str()); + } + + send_status(req, from, fromlen); + conn->register_status = Srs28181RegisterOk; + conn->register_time = srs_get_system_time(); + conn->reg_expires = req->expires; + + + }else if (req->is_message()) { + SrsGb28181Conn* conn = fetch(req); + if (!conn){ + srs_trace("gb28181: %s client not found", req->sip_auth_id.c_str()); + return srs_success; + } + + if (conn->register_status == Srs28181Unkonw) { + send_bye(req, from, fromlen); + srs_trace("gb28181: %s client not register", req->sip_auth_id.c_str()); + return srs_success; + } + + send_status(req, from, fromlen); + conn->alive_status = Srs28181AliveOk; + conn->alive_time = srs_get_system_time(); + + if (conn->register_status == Srs28181RegisterOk && + conn->alive_status == Srs28181AliveOk && + conn->invite_status != Srs28181InviteOk) + { + int lpm = 0; + if (alloc_port(&lpm) != srs_success) { + return srs_error_wrap(err, "alloc port"); + } + + if (lpm){ + send_invite(req, from, fromlen, lpm); + conn->rtmp_url = config->output; + conn->start_rtp_listen(lpm); + conn->invite_status == Srs28181Trying; + conn->invite_time = srs_get_system_time(); + } + + } + + }else if (req->is_invite()) { + SrsGb28181Conn* conn = fetch(req); + + 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 (!conn){ + send_bye(req, from, fromlen); + srs_trace("gb28181: %s client not found", req->sip_auth_id.c_str()); + return srs_success; + } + + if (conn->register_status == Srs28181Unkonw || + conn->alive_status == Srs28181Unkonw) { + send_bye(req, from, fromlen); + srs_trace("gb28181: %s client not register or alive", req->sip_auth_id.c_str()); + return srs_success; + } + + 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); + conn->invite_status = Srs28181InviteOk; + conn->invite_time = srs_get_system_time(); + } + }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()); + + SrsGb28181Conn* conn = fetch(req); + send_status(req, from, fromlen); + + if (!conn){ + srs_trace("gb28181: %s client not found", req->sip_auth_id.c_str()); + return srs_success; + } + + conn->stop_rtp_listen(); + conn->invite_status = Srs28181Bye; + conn->invite_time = 0; + + }else{ + srs_trace("gb28181: ingor request method=%s", req->method.c_str()); + } + + return err; +} + +srs_error_t SrsGb28181Caster::send_message(sockaddr* from, int fromlen, std::stringstream& ss) +{ + srs_error_t err = srs_success; + + 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){ + return srs_error_wrap(err, "gb28181: send_message falid"); + } + + return err; +} + +srs_error_t SrsGb28181Caster::send_bye(SrsSipRequest *req, sockaddr *f, int l) +{ + srs_error_t err = srs_success; + srs_assert(req); + + std::stringstream ss; + + req->host = config->sip_host; + req->host_port = config->listen_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + sip->req_bye(ss, req); + send_message(f, l, ss); + + return err; + +} +srs_error_t SrsGb28181Caster::send_ack(SrsSipRequest *req, sockaddr *f, int l) +{ + srs_error_t err = srs_success; + srs_assert(req); + + std::stringstream ss; + + req->host = config->sip_host; + req->host_port = config->listen_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + sip->resp_ack(ss, req); + send_message(f, l, ss); + + return err; +} + +srs_error_t SrsGb28181Caster::send_invite(SrsSipRequest *req, sockaddr *f, int l, int port) +{ + srs_error_t err = srs_success; + srs_assert(req); + + std::stringstream ss; + + req->host = config->sip_host; + req->host_port = config->listen_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + sip->req_invite(ss, req, port); + send_message(f, l, ss); + + return err; + +} + +srs_error_t SrsGb28181Caster::send_status(SrsSipRequest *req, sockaddr *f, int l) +{ + srs_error_t err = srs_success; + srs_assert(req); + + std::stringstream ss; + + req->host = config->sip_host; + req->host_port = config->listen_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + + sip->resp_status(ss, req); + send_message(f, l, ss); + + return err; + +} + + +srs_error_t SrsGb28181Caster::fetch_or_create(SrsSipRequest* r, SrsGb28181Conn** gb28181) +{ + srs_error_t err = srs_success; + + SrsGb28181Conn* conn = NULL; + if ((conn = fetch(r)) != NULL) { + *gb28181 = conn; + return err; + } + + string key = r->sip_auth_id; + conn = new SrsGb28181Conn(this, key); + conn->set_request_info(r); + if ((err = conn->serve()) != srs_success) { + return srs_error_wrap(err, "sipconn serve %s", key.c_str()); + } + clients[key] = conn; + *gb28181 = conn; + + return err; +} + +SrsGb28181Conn* SrsGb28181Caster::fetch(const SrsSipRequest* r) +{ + SrsGb28181Conn* conn = NULL; + + string key = r->sip_auth_id; + if (clients.find(key) == clients.end()) { + return NULL; + } + + conn = clients[key]; + return conn; +} + + +void SrsGb28181Caster::destroy() +{ + std::map::iterator it; + for (it = clients.begin(); it != clients.end(); ++it) { + SrsGb28181Conn* conn = it->second; + manager->remove(conn); + } + clients.clear(); +} + +void SrsGb28181Caster::remove(SrsGb28181Conn* conn) +{ + std::string id = conn->get_session_id(); + map::iterator key = clients.find(id); + if (key != clients.end()) { + clients.erase(key); + } + manager->remove(conn); +} diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp new file mode 100644 index 000000000..3d8bd13b0 --- /dev/null +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -0,0 +1,330 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * 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 + +class SrsStSocket; +class SrsRtpConn; +class SrsRtspConn; +class SrsRtspStack; +class SrsRtspCaster; +class SrsConfDirective; +class SrsRtpPacket; +class SrsRequest; +class SrsStSocket; +class SrsRtmpClient; +class SrsRawH264Stream; +class SrsRawAacStream; +struct SrsRawAacStreamCodec; +class SrsSharedPtrMessage; +class SrsAudioFrame; +class SrsSimpleStream; +class SrsPithyPrint; +class SrsSimpleRtmpClient; +class SrsSipStack; +class SrsGb28181Caster; +class SrsRtspJitter; +class SrsRtspAudioCache; +class SrsSipRequest; +class SrsGb28181Conn; +class SrsGb28281ClientInfo; + +/* 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; +}; + + + +class SrsPsRtpPacket: public SrsRtpPacket +{ +public: + SrsPsRtpPacket(); + virtual ~SrsPsRtpPacket(); +public: + virtual srs_error_t decode(SrsBuffer* stream); +}; + +// A rtp connection which transport a stream. +class SrsPsRtpConn: public ISrsUdpHandler +{ +private: + SrsPithyPrint* pprint; + SrsUdpListener* listener; + SrsGb28181Conn* gb28181; + SrsPsRtpPacket* cache; + std::map cache_payload; + std::string session_id; + int _port; + uint32_t pre_timestamp; + + SrsFileWriter ps_fw; + SrsFileWriter video_fw; + SrsFileWriter audio_fw; + + bool first_keyframe_flag; + bool wait_first_keyframe; + bool audio_enable; + +public: + SrsPsRtpConn(SrsGb28181Conn* r, int p, std::string sid, bool a, bool k); + virtual ~SrsPsRtpConn(); + +private: + int64_t parse_ps_timestamp(const uint8_t* p); + +private: + bool can_send_ps_av_packet(); + void dispose(); +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); + virtual srs_error_t on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp); +}; + +class SrsGb28281ClientInfo { +public: + SrsGb28281ClientInfo(); + virtual ~SrsGb28281ClientInfo(); + +public: + sockaddr* sock_from; + int sock_fromlen; + srs_netfd_t stfd; + SrsSipRequest *req; +}; + +enum Srs28181CtrlStatusType{ + Srs28181Unkonw = 0, + Srs28181RegisterOk = 1, + Srs28181AliveOk = 2, + Srs28181InviteOk = 3, + Srs28181Trying = 4, + Srs28181Bye = 5, +}; + +class SrsGb28181Conn : public ISrsCoroutineHandler, public ISrsConnection +{ +private: + std::string output_template; + SrsPithyPrint* pprint; +public: + Srs28181CtrlStatusType register_status; + Srs28181CtrlStatusType alive_status; + Srs28181CtrlStatusType invite_status; + srs_utime_t register_time; + srs_utime_t alive_time; + srs_utime_t invite_time; + srs_utime_t recv_rtp_time; + + std::string rtmp_url; + int reg_expires; + +private: + std::string session_id; + // video stream. + int video_id; + std::string video_codec; + SrsPsRtpConn* video_rtp; + // audio stream. + int audio_id; + std::string audio_codec; + int audio_sample_rate; + int audio_channel; + SrsPsRtpConn* audio_rtp; +public: + SrsGb28281ClientInfo* info; +private: + SrsStSocket* skt; + SrsSipStack* sip; + SrsGb28181Caster* caster; + SrsCoroutine* trd; +private: + SrsSipRequest* req; + SrsSimpleRtmpClient* sdk; + SrsRtspJitter* vjitter; + SrsRtspJitter* ajitter; +private: + SrsRawH264Stream* avc; + std::string h264_sps; + std::string h264_pps; + bool h264_sps_changed; + bool h264_pps_changed; + bool h264_sps_pps_sent; +private: + SrsRawAacStream* aac; + std::string aac_specific_config; +public: + SrsGb28181Conn(SrsGb28181Caster* c, std::string id); + virtual ~SrsGb28181Conn(); +public: + virtual srs_error_t serve(); + virtual std::string remote_ip(); + virtual void set_request_info(SrsSipRequest *req); + virtual std::string get_session_id(); +private: + virtual srs_error_t do_cycle(); +// internal methods +public: + virtual srs_error_t start_rtp_listen(int port); + virtual srs_error_t stop_rtp_listen(); +// Interface ISrsOneCycleThreadHandler +public: + virtual srs_error_t cycle(); +public: + virtual srs_error_t on_rtp_video(SrsSimpleStream* stream, int64_t dts, int keyframe); + 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(); +}; + +class SrsGb28181Config +{ +public: + std::string sip_host; + std::string sip_port; + std::string sip_serial; + std::string sip_realm; + int sip_ack_timeout; + int sip_keepalive_timeout; + int rtp_idle_timeout; + bool audio_enable; + std::string output; + int rtp_port_min; + int rtp_port_max; + int listen_port; + bool print_sip_message; + bool wait_keyframe; +public: + SrsGb28181Config(SrsConfDirective* c); + virtual ~SrsGb28181Config(); +}; + +//gb28181 conn manager +class SrsGb28181Caster : public ISrsUdpHandler +{ +private: + SrsGb28181Config *config; + // The key: port, value: whether used. + std::map used_ports; + SrsSipStack *sip; + srs_netfd_t lfd; +private: + std::map clients; + SrsCoroutineManager* manager; + +public: + SrsGb28181Caster(SrsConfDirective* c); + virtual ~SrsGb28181Caster(); + +private: + srs_error_t fetch_or_create(SrsSipRequest* r, SrsGb28181Conn** gb28181); + virtual SrsGb28181Conn* fetch(const SrsSipRequest* r); + virtual void destroy(); +public: + // Alloc a rtp port from local ports pool. + // @param pport output the rtp port. + virtual srs_error_t alloc_port(int* pport); + // Free the alloced rtp port. + virtual void free_port(int lpmin, int lpmax); + virtual srs_error_t initialize(); + + virtual void set_stfd(srs_netfd_t fd); + virtual SrsGb28181Config GetGb28181Config(); + +// Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); +private: + virtual srs_error_t on_udp_bytes(std::string host, int port, char* buf, int nb_buf, sockaddr* from, int fromlen); +// internal methods. +public: + virtual srs_error_t send_message(sockaddr* f, int l, std::stringstream& ss); + virtual srs_error_t send_bye(SrsSipRequest *req, sockaddr *f, int l); + virtual srs_error_t send_ack(SrsSipRequest *req, sockaddr *f, int l); + virtual srs_error_t send_invite(SrsSipRequest *req, sockaddr *f, int l, int port); + virtual srs_error_t send_status(SrsSipRequest *req, sockaddr *f, int l); + virtual void remove(SrsGb28181Conn* conn); +}; + +#endif + diff --git a/trunk/src/app/srs_app_listener.cpp b/trunk/src/app/srs_app_listener.cpp index b33b4f6f8..0634e2855 100755 --- a/trunk/src/app/srs_app_listener.cpp +++ b/trunk/src/app/srs_app_listener.cpp @@ -59,6 +59,13 @@ srs_error_t ISrsUdpHandler::on_stfd_change(srs_netfd_t /*fd*/) return srs_success; } +void ISrsUdpHandler::set_stfd(srs_netfd_t /*fd*/) +{ + +} + + + ISrsTcpHandler::ISrsTcpHandler() { } @@ -104,6 +111,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); @@ -206,4 +215,3 @@ srs_error_t SrsTcpListener::cycle() return err; } - diff --git a/trunk/src/app/srs_app_listener.hpp b/trunk/src/app/srs_app_listener.hpp index d7d930e91..5f112ae12 100644 --- a/trunk/src/app/srs_app_listener.hpp +++ b/trunk/src/app/srs_app_listener.hpp @@ -43,6 +43,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 2edc525aa..26106feaa 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -51,6 +51,7 @@ using namespace std; #include #include #include +#include // system interval in srs_utime_t, // all resolution times should be times togother, @@ -108,6 +109,8 @@ std::string srs_listener_type2string(SrsListenerType type) return "RTSP"; case SrsListenerFlv: return "HTTP-FLV"; + case SrsListenerGb28181: + return "GB28181-SIP over UDP"; default: return "UNKONWN"; } @@ -298,7 +301,7 @@ 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 == SrsListenerGb28181); ip = i; port = p; @@ -336,6 +339,22 @@ 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 == SrsListenerGb28181); + if (type == SrsListenerGb28181) { + caster = new SrsGb28181Caster(c); + } +} + +SrsGb28181Listener::~SrsGb28181Listener() +{ + srs_freep(caster); +} + SrsSignalManager* SrsSignalManager::instance = NULL; SrsSignalManager::SrsSignalManager(SrsServer* s) @@ -1093,6 +1112,8 @@ 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)) { + listener = new SrsGb28181Listener(this, SrsListenerGb28181, stream_caster); } else { return srs_error_new(ERROR_STREAM_CASTER_ENGINE, "invalid caster %s", caster.c_str()); } diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index f43d7fc04..46127b6c6 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -69,6 +69,8 @@ enum SrsListenerType SrsListenerRtsp = 4, // TCP stream, FLV stream over HTTP. SrsListenerFlv = 5, + // UDP stream, gb28181 stream + SrsListenerGb28181 = 6, }; // A common tcp listener, for RTMP/HTTP server. @@ -156,6 +158,14 @@ public: virtual ~SrsUdpCasterListener(); }; +// A UDP sip listener, for sip 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 diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp new file mode 100644 index 000000000..aed80de1e --- /dev/null +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -0,0 +1,553 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * 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 + +#define SIP_MAX_HEADER_LEN 2049 + +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-1); + return msg; +} + + +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; + 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; + 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_trace("sip: header=%s\n", header.c_str()); + //srs_trace("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_trace("=== 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:")) { + srs_trace("sip: message head %s content=%s", phead, content.c_str()); + } + else if (!strcasecmp(phead, "content-type:")) { + srs_trace("sip: message head %s content=%s", phead, content.c_str()); + } + 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:")) { + 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 { + srs_trace("sip: unkonw message head %s content=%s", phead, content.c_str()); + } + } + } + //srs_trace("====new line=%s", oneline.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_trace("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_trace("via=%s", req->via.c_str()); + // srs_trace("via_branch=%s", req->branch.c_str()); + //srs_trace("cseq=%d", req->seq); + // srs_trace("contact=%s", req->contact.c_str()); + //srs_trace("from=%s", req->from.c_str()); + //srs_trace("to=%s", req->to.c_str()); + //srs_trace("callid=%s", req->call_id.c_str()); + // srs_trace("status=%s", req->status.c_str()); + // srs_trace("from_tag=%s", req->from_tag.c_str()); + // srs_trace("to_tag=%s", req->to_tag.c_str()); + //srs_trace("sip_auth_id=%s", req->sip_auth_id.c_str()); + + return err; +} + +srs_error_t 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; + + return srs_success; +} + +srs_error_t SrsSipStack::resp_ack(std::stringstream& ss, SrsSipRequest *req){ + + 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 << ";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 << " " << req->method << SRS_RTSP_CRLF + << "Max-Forwards: 70" << SRS_RTSP_CRLF + << "User-Agent: "<< SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; + + return srs_success; +} + +srs_error_t SrsSipStack::resp_status(stringstream& ss, SrsSipRequest *req) +{ + srs_error_t err = srs_success; + + if (req->method == "REGISTER"){ + 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 + << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; + }else{ + 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; + } + + return err; +} + +srs_error_t SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, int port) +{ + /* + INVITE sip:34020000001320000001@3402000000 SIP/2.0 + Via: SIP/2.0/UDP 192.168.1.22:15060;rport;branch=z9hG4bK369961166 + From: ;tag=536961166 + To: + Call-ID: 929961057 + CSeq: 3 INVITE + Content-Type: APPLICATION/SDP + Contact: + Max-Forwards: 70 + User-Agent: XXXXXXX XXXXXXX + Subject: 34020000001320000001:0200000001,34020000002020000001:0 + Content-Length: 247 + + v=0 + o=34020000002000000001 0 0 IN IP4 192.168.1.23 + s=Play + c=IN IP4 192.168.1.23 + t=0 0 + m=video 30000 RTP/AVP 96 97 98 99 + a=recvonly + a=rtpmap:96 PS/90000 + a=rtpmap:97 MPEG4/90000 + a=rtpmap:98 H264/90000 + a=rtpmap:99 H265/90000 + y=0200000001 + */ + + srs_error_t err = srs_success; + int ssrc = srs_sip_random(10000, 99999); + std::stringstream sdp; + sdp << "v=0" << SRS_RTSP_CRLF + << "o=" << req->sip_auth_id << " 0 0 IN IP4 " << req->host << SRS_RTSP_CRLF + << "s=Play" << SRS_RTSP_CRLF + << "c=IN IP4 " << req->host << SRS_RTSP_CRLF + << "t=0 0" << SRS_RTSP_CRLF + << "m=video " << port <<" RTP/AVP 96 97 98 99" << SRS_RTSP_CRLF + << "a=recvonly" << SRS_RTSP_CRLF + << "a=rtpmap:96 PS/90000" << SRS_RTSP_CRLF + << "a=rtpmap:97 MPEG4/90000" << SRS_RTSP_CRLF + << "a=rtpmap:98 H264/90000" << SRS_RTSP_CRLF + << "a=rtpmap:99 H265/90000" << SRS_RTSP_CRLF + << "y=00181" << ssrc << SRS_RTSP_CRLF; + + //<< "a=streamMode:MAIN\r\n" + //<< "a=filesize:0\r\n" + + + int rand = srs_sip_random(1000, 9999); + std::stringstream from, to, uri; + //"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(); + + ss << "INVITE " << req->uri << " " << SRS_SIP_VERSION << SRS_RTSP_CRLF + << "Via: " << SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport;branch=z9hG4bK3420" << rand << SRS_RTSP_CRLF + << "From: from << ">;tag=51235" << rand << 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 << ":00181" << ssrc << "," << req->serial << ":0" << SRS_RTSP_CRLF + << "Content-Length: " << sdp.str().length() << SRS_RTSP_CRLFCRLF + << sdp.str(); + + return err; +} + +srs_error_t SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) +{ + srs_error_t err = srs_success; + + std::stringstream from, to, uri; + uri << "sip:" << req->sip_auth_id << "@" << req->realm; + 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(); + + int rand = srs_sip_random(1000, 9999); + ss << "BYE " << req->uri << " "<< SRS_SIP_VERSION << SRS_RTSP_CRLF + << "Via: "<< SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";branch=z9hG4bK3420" << rand << SRS_RTSP_CRLF + << "From: from << ">;tag=51235" << rand << SRS_RTSP_CRLF + << "To: to << ">" << SRS_RTSP_CRLF + << "Call-ID: 20000" << rand << SRS_RTSP_CRLF + << "CSeq: 21 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; + + return err; +} + + +#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..f4868a724 --- /dev/null +++ b/trunk/src/protocol/srs_sip_stack.hpp @@ -0,0 +1,143 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * 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; + +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 srs_error_t resp_status(std::stringstream& ss, SrsSipRequest *req); + virtual srs_error_t resp_keepalive(std::stringstream& ss, SrsSipRequest *req); + virtual srs_error_t resp_ack(std::stringstream& ss, SrsSipRequest *req); + + virtual srs_error_t req_invite(std::stringstream& ss, SrsSipRequest *req, int port); + virtual srs_error_t req_bye(std::stringstream& ss, SrsSipRequest *req); + +}; + +#endif + +#endif + diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index f63cd4279..27d860efb 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -397,6 +397,11 @@ int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, in return st_recvfrom((st_netfd_t)stfd, buf, len, from, fromlen, (st_utime_t)timeout); } +int srs_sendto(srs_netfd_t stfd, void *msg, int len, struct sockaddr *to, int tolen, srs_utime_t timeout) +{ + return st_sendto((st_netfd_t)stfd, (const void*)msg, len, (const struct sockaddr *)to, tolen, (st_utime_t)timeout); +} + srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout) { return (srs_netfd_t)st_accept((st_netfd_t)stfd, addr, addrlen, (st_utime_t)timeout); diff --git a/trunk/src/service/srs_service_st.hpp b/trunk/src/service/srs_service_st.hpp index 510b9ba8a..234b3a9a8 100644 --- a/trunk/src/service/srs_service_st.hpp +++ b/trunk/src/service/srs_service_st.hpp @@ -88,6 +88,7 @@ extern srs_netfd_t srs_netfd_open_socket(int osfd); extern srs_netfd_t srs_netfd_open(int osfd); extern int srs_recvfrom(srs_netfd_t stfd, void *buf, int len, struct sockaddr *from, int *fromlen, srs_utime_t timeout); +extern int srs_sendto(srs_netfd_t stfd, void *msg, int len, struct sockaddr *to, int tolen, srs_utime_t timeout); extern srs_netfd_t srs_accept(srs_netfd_t stfd, struct sockaddr *addr, int *addrlen, srs_utime_t timeout); From 8b4f84e3364fb4ac8a646e4d287f82b78d8c5cd1 Mon Sep 17 00:00:00 2001 From: xialixin Date: Fri, 20 Mar 2020 10:38:38 +0800 Subject: [PATCH 02/13] fix push.gb28181.conf, gb28181conn repeat call serve() --- trunk/conf/push.gb28181.conf | 5 +++-- trunk/src/app/srs_app_gb28181.cpp | 29 ++++++++++++++-------------- trunk/src/protocol/srs_sip_stack.cpp | 7 +++++-- 3 files changed, 23 insertions(+), 18 deletions(-) diff --git a/trunk/conf/push.gb28181.conf b/trunk/conf/push.gb28181.conf index b76887d98..cb2f848b2 100644 --- a/trunk/conf/push.gb28181.conf +++ b/trunk/conf/push.gb28181.conf @@ -3,7 +3,8 @@ listen 1935; max_connections 1000; daemon off; -pid ./objs/srs28181.pid +pid ./objs/srs28181.pid; +srs_log_file ./objs/srs28181.log; srs_log_tank console; stream_caster { enabled on; @@ -23,7 +24,7 @@ stream_caster { #服务器端编号 #设备端配置编号需要与该值一致,否则无法注册 - serial 34020000002020000001; + serial 34020000002000000001; #服务器端域 realm 3402000000; diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index b4d647ce1..46f7950ff 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -571,8 +571,7 @@ srs_error_t SrsGb28181Conn::do_cycle() pprint->elapse(); if ((err = trd->pull()) != srs_success) { - //srs_trace("pull faild %s")); - //return srs_error_wrap(err, "sip cycle"); + return srs_error_wrap(err, "gb28181conn cycle"); } srs_utime_t now = srs_get_system_time(); @@ -616,8 +615,11 @@ srs_error_t SrsGb28181Conn::do_cycle() } if (pprint->can_print()) { - srs_trace("gb28181: client id=%s, status, druation reg=%u alive=%u invite=%u", + srs_trace("gb28181: client id=%s, druation reg=%u alive=%u invite=%u", session_id.c_str(), reg_duration, alive_duration, invite_duration); + + srs_trace("gb28181: client id=%s, status reg_status=%u alive_status=%u invite_status=%u", + session_id.c_str(), register_status, alive_status, invite_status); } srs_usleep(1000 * 1000); @@ -1188,12 +1190,11 @@ srs_error_t SrsGb28181Caster::on_udp_bytes(string peer_ip, int peer_port, } srs_assert(conn != NULL); - if (conn->register_status == Srs28181Unkonw) - { - conn->serve(); - }else{ - srs_trace("gb28181: %s client is register", req->sip_auth_id.c_str()); - } + // if (conn->register_status == Srs28181Unkonw) + // { + // }else{ + // srs_trace("gb28181: %s client is register", req->sip_auth_id.c_str()); + // } send_status(req, from, fromlen); conn->register_status = Srs28181RegisterOk; @@ -1204,13 +1205,13 @@ srs_error_t SrsGb28181Caster::on_udp_bytes(string peer_ip, int peer_port, }else if (req->is_message()) { SrsGb28181Conn* conn = fetch(req); if (!conn){ - srs_trace("gb28181: %s client not found", req->sip_auth_id.c_str()); + srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); return srs_success; } if (conn->register_status == Srs28181Unkonw) { send_bye(req, from, fromlen); - srs_trace("gb28181: %s client not register", req->sip_auth_id.c_str()); + srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); return srs_success; } @@ -1247,14 +1248,14 @@ srs_error_t SrsGb28181Caster::on_udp_bytes(string peer_ip, int peer_port, if (!conn){ send_bye(req, from, fromlen); - srs_trace("gb28181: %s client not found", req->sip_auth_id.c_str()); + srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); return srs_success; } if (conn->register_status == Srs28181Unkonw || conn->alive_status == Srs28181Unkonw) { send_bye(req, from, fromlen); - srs_trace("gb28181: %s client not register or alive", req->sip_auth_id.c_str()); + srs_trace("gb28181: %s client not registered or not alive", req->sip_auth_id.c_str()); return srs_success; } @@ -1274,7 +1275,7 @@ srs_error_t SrsGb28181Caster::on_udp_bytes(string peer_ip, int peer_port, send_status(req, from, fromlen); if (!conn){ - srs_trace("gb28181: %s client not found", req->sip_auth_id.c_str()); + srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); return srs_success; } diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp index aed80de1e..e3031fd20 100644 --- a/trunk/src/protocol/srs_sip_stack.cpp +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -297,10 +297,10 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m srs_trace("sip: message head %s content=%s", phead, content.c_str()); } else if (!strcasecmp(phead, "content-length:")) { - srs_trace("sip: message head %s content=%s", phead, content.c_str()); + req->content_length = strtoul(content.c_str(), NULL, 10); } else if (!strcasecmp(phead, "content-type:")) { - srs_trace("sip: message head %s content=%s", phead, content.c_str()); + req->content_type = content; } else if (!strcasecmp(phead, "cseq:")) { std::vector vec_seq = srs_string_split(content, " "); @@ -331,6 +331,9 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m 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 { srs_trace("sip: unkonw message head %s content=%s", phead, content.c_str()); } From c99fb99ab6e39634e035a30180c75a8a00aeb4c8 Mon Sep 17 00:00:00 2001 From: xialixin Date: Tue, 31 Mar 2020 00:39:10 +0800 Subject: [PATCH 03/13] refactor gb28181, supporting SIP server enable, multiplex, API interface --- trunk/conf/push.gb28181.conf | 99 +- trunk/configure | 2 +- trunk/src/app/srs_app_config.cpp | 144 ++- trunk/src/app/srs_app_config.hpp | 7 +- trunk/src/app/srs_app_gb28181.cpp | 1358 ++++++++++++++----------- trunk/src/app/srs_app_gb28181.hpp | 438 +++++--- trunk/src/app/srs_app_gb28181_sip.cpp | 471 +++++++++ trunk/src/app/srs_app_gb28181_sip.hpp | 161 +++ trunk/src/app/srs_app_http_api.cpp | 125 +++ trunk/src/app/srs_app_http_api.hpp | 10 + trunk/src/app/srs_app_server.cpp | 70 +- trunk/src/app/srs_app_server.hpp | 13 +- trunk/src/kernel/srs_kernel_error.hpp | 16 + trunk/src/protocol/srs_sip_stack.cpp | 406 ++++++-- trunk/src/protocol/srs_sip_stack.hpp | 14 +- 15 files changed, 2439 insertions(+), 895 deletions(-) create mode 100644 trunk/src/app/srs_app_gb28181_sip.cpp create mode 100644 trunk/src/app/srs_app_gb28181_sip.hpp diff --git a/trunk/conf/push.gb28181.conf b/trunk/conf/push.gb28181.conf index cb2f848b2..f7a3779b6 100644 --- a/trunk/conf/push.gb28181.conf +++ b/trunk/conf/push.gb28181.conf @@ -6,28 +6,35 @@ daemon off; pid ./objs/srs28181.pid; srs_log_file ./objs/srs28181.log; srs_log_tank console; + +http_api { + enabled on; + listen 1985; +} + stream_caster { enabled on; caster gb28181; - #rtmp输出地址,可以参数化 - #[stream] 代表客户端sip设备编号 - #[timestamp] 时间戳 - output rtmp://127.0.0.1/live/[stream]; - #sip监听udp端口 - listen 15060; + #转发流到rtmp服务器地址与端口 + output 127.0.0.1:1935; - #服务器主机号,可以域名或ip地址 - #也就是设备端将媒体发送的地址,如果是服务器是内外网 - #需要写外网地址 - host 192.168.1.27; + #接收设备端rtp流的多路复用端口 + listen 9000; - #服务器端编号 - #设备端配置编号需要与该值一致,否则无法注册 - serial 34020000002000000001; + #rtp接收监听端口范围,最小值 + rtp_port_min 58200; + #rtp接收监听端口范围,最大值 + rtp_port_max 58300; - #服务器端域 - realm 3402000000; + #是否等待关键帧之后,再转发, + #off:不需等待,直接转发 + #on:等第一个关键帧后,再转发 + wait_keyframe off; + + #rtp包空闲等待时间,如果指定时间没有收到任何包 + #rtp监听连接自动停止,发送BYE命令 + rtp_idle_timeout 30; #是否转发音频流 #目前只支持aac格式,所以需要设备支持aac格式 @@ -41,32 +48,50 @@ stream_caster { #因为flash,只支持11025 22050 44100 audio_enable on; - #服务端发送ack后,接收回应的超时时间,单位为秒 - #如果指定时间没有回应,认为失败 - ack_timeout 30; + #服务器主机号,可以域名或ip地址 + #也就是设备端将媒体发送的地址,如果是服务器是内外网 + #需要写外网地址, + #调用api创建stream session时返回ip地址也是host + host 192.168.1.27; - #设备心跳维持时间,如果指定时间内(秒)没有接收一个心跳 - #认为设备离线 - keepalive_timeout 30; + sip { + #是否启用srs内部sip信令 + #为on信令走srs, off 只转发ps流 + enabled on; + + #sip监听udp端口 + listen 15060; + + #服务器端编号 + #设备端配置编号需要与该值一致,否则无法注册 + serial 34020000002000000001; - #是否等待关键帧之后,再转发, - #off:不需等待,直接转发 - #on:等第一个关键帧后,再转发 - wait_keyframe off; + #服务器端域 + realm 3402000000; - #日志打印是否打印sip信息 - #off:不打印 - #on:打印接收或发送sip命令信息 - print_sip_message off; - - #rtp包空闲等待时间,如果指定时间没有收到任何包 - #rtp监听连接自动停止,发送BYE命令 - rtp_idle_timeout 30; + #服务端发送ack后,接收回应的超时时间,单位为秒 + #如果指定时间没有回应,认为失败 + ack_timeout 30; - #rtp接收监听端口范围,最小值 - rtp_port_min 58200; - #rtp接收监听端口范围,最大值 - rtp_port_max 58300; + #设备心跳维持时间,如果指定时间内(秒)没有接收一个心跳 + #认为设备离线 + keepalive_timeout 30; + + #日志打印是否打印sip信息 + #off:不打印 + #on:打印接收或发送sip命令信息 + 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 0b50507e7..d4369ef30 100755 --- a/trunk/configure +++ b/trunk/configure @@ -257,7 +257,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_gb28181") + "srs_app_coworkers" "srs_app_hybrid" "srs_app_gb28181" "srs_app_gb28181_sip") DEFINES="" # add each modules for app for SRS_MODULE in ${SRS_MODULES[*]}; do diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index bb4fc793a..d7ca83ace 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -2160,6 +2160,10 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj) 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()); } } @@ -3672,12 +3676,24 @@ srs_error_t SrsConfig::check_normal_config() string n = conf->name; if (n != "enabled" && n != "caster" && n != "output" && n != "listen" && n != "rtp_port_min" && n != "rtp_port_max" - && n != "rtp_idle_timeout" && n != "ack_timeout" && n != "keepalive_timeout" - && n != "host" && n != "serial" && n != "realm" + && n != "rtp_idle_timeout" && n != "sip" && n != "audio_enable" && n != "wait_keyframe" - && n != "print_sip_message") { + && 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()); + } + } + } } } @@ -4207,7 +4223,7 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf) return ::atoi(conf->arg0().c_str()); } -int SrsConfig::get_stream_caster_gb28181_rtp_ide_timeout(SrsConfDirective* conf) +int SrsConfig::get_stream_caster_gb28181_rtp_idle_timeout(SrsConfDirective* conf) { static int DEFAULT = 30; @@ -4215,7 +4231,7 @@ int SrsConfig::get_stream_caster_gb28181_rtp_ide_timeout(SrsConfDirective* conf) return DEFAULT; } - conf = conf->get("rtp_ide_timeout"); + conf = conf->get("rtp_idle_timeout"); if (!conf || conf->arg0().empty()) { return DEFAULT; } @@ -4230,6 +4246,11 @@ int SrsConfig::get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf) if (!conf) { return DEFAULT; } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } conf = conf->get("ack_timeout"); if (!conf || conf->arg0().empty()) { @@ -4246,6 +4267,11 @@ int SrsConfig::get_stream_caster_gb28181_keepalive_timeout(SrsConfDirective* con if (!conf) { return DEFAULT; } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } conf = conf->get("keepalive_timeout"); if (!conf || conf->arg0().empty()) { @@ -4257,12 +4283,12 @@ int SrsConfig::get_stream_caster_gb28181_keepalive_timeout(SrsConfDirective* con string SrsConfig::get_stream_caster_gb28181_host(SrsConfDirective* conf) { - static string DEFAULT = "127.0.0.1"; + static string DEFAULT = ""; if (!conf) { return DEFAULT; } - + conf = conf->get("host"); if (!conf || conf->arg0().empty()) { return DEFAULT; @@ -4278,6 +4304,11 @@ string SrsConfig::get_stream_caster_gb28181_serial(SrsConfDirective* conf) if (!conf) { return DEFAULT; } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } conf = conf->get("serial"); if (!conf || conf->arg0().empty()) { @@ -4294,6 +4325,11 @@ string SrsConfig::get_stream_caster_gb28181_realm(SrsConfDirective* conf) if (!conf) { return DEFAULT; } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } conf = conf->get("realm"); if (!conf || conf->arg0().empty()) { @@ -4326,6 +4362,11 @@ bool SrsConfig::get_stream_caster_gb28181_print_sip_message(SrsConfDirective* co if (!conf) { return DEFAULT; } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } conf = conf->get("print_sip_message"); if (!conf || conf->arg0().empty()) { @@ -4351,6 +4392,95 @@ bool SrsConfig::get_stream_caster_gb28181_wait_keyframe(SrsConfDirective* conf) return SRS_CONF_PERFER_FALSE(conf->arg0()); } +bool SrsConfig::get_stream_caster_gb28181_sip_enable(SrsConfDirective* conf) +{ + static bool DEFAULT = false; + + 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 = false; + + 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 = 0; + + 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()); + +} + + SrsConfDirective* SrsConfig::get_vhost(string vhost, bool try_default_vhost) { srs_assert(root); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index ffae3baff..3e41e23df 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -486,7 +486,7 @@ public: // Get the max udp port for rtp of stream caster rtsp. virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf); - virtual int get_stream_caster_gb28181_rtp_ide_timeout(SrsConfDirective* conf); + virtual int 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); @@ -495,6 +495,11 @@ public: 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); + // vhost specified section public: diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index 46f7950ff..39b1714bf 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -32,6 +32,7 @@ using namespace std; #include +#include #include #include #include @@ -52,7 +53,7 @@ using namespace std; #include #include #include -#include + //#define W_PS_FILE @@ -94,170 +95,252 @@ srs_error_t SrsPsRtpPacket::decode(SrsBuffer* stream) // video codec. if (payload_type == 96) { - // ps stream atleast 4bytes content. - if (!stream->require(4)) { - return srs_error_new(ERROR_RTP_TYPE96_CORRUPT, "requires 4 only %d bytes", stream->left()); + // 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; } - -SrsPsRtpConn::SrsPsRtpConn(SrsGb28181Conn* conn, int p, std::string sid, bool b, bool k) +//SrsPsRtpListener +SrsPsRtpListener::SrsPsRtpListener(SrsGb28181Config* c, int p, std::string s) { - gb28181 = conn; + rtp_processor = new SrsGb28181PsRtpProcessor(c, s); _port = p; - session_id = sid; // TODO: support listen at <[ip:]port> listener = new SrsUdpListener(this, srs_any_address_for_listener(), p); - cache = new SrsPsRtpPacket(); - pprint = SrsPithyPrint::create_caster(); - pre_timestamp = -1; - - audio_enable = b; - first_keyframe_flag = false; - wait_first_keyframe = k; } -SrsPsRtpConn::~SrsPsRtpConn() +SrsPsRtpListener::~SrsPsRtpListener() { - ps_fw.close(); - video_fw.close(); - audio_fw.close(); - - dispose(); - srs_freep(listener); - srs_freep(cache); - srs_freep(pprint); + srs_freep(rtp_processor); } -int SrsPsRtpConn::port() +int SrsPsRtpListener::port() { return _port; } -srs_error_t SrsPsRtpConn::listen() +srs_error_t SrsPsRtpListener::listen() { return listener->listen(); } -void SrsPsRtpConn::dispose() -{ - map::iterator it; - for (it = cache_payload.begin(); it != cache_payload.end(); ++it) { - srs_freep(it->second); +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; +} - cache_payload.clear(); - return; +//SrsGb28181RtpMuxService +SrsGb28181RtpMuxService::SrsGb28181RtpMuxService(SrsConfDirective* c) +{ + config = new SrsGb28181Config(c); + rtp_processor = new SrsGb28181PsRtpProcessor(config,""); } -int64_t SrsPsRtpConn::parse_ps_timestamp(const uint8_t* p) +SrsGb28181RtpMuxService::~SrsGb28181RtpMuxService() { - unsigned long b; - //total 33 bits - unsigned long val, val2, val3; + srs_freep(config); + srs_freep(rtp_processor); +} - //1st byte, 5、6、7 bit - b = *p++; - val = (b & 0x0e); +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; +} - //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; +//SrsGb28181PsRtpProcessor +SrsGb28181PsRtpProcessor::SrsGb28181PsRtpProcessor(SrsGb28181Config* c, std::string id) +{ + config = c; + pprint = SrsPithyPrint::create_caster(); + channel_id = id; +} - //<32--val--30> <29----val2----15> <14----val3----0> - val = (val << 29) | (val2 << 15) | val3; - return val; +SrsGb28181PsRtpProcessor::~SrsGb28181PsRtpProcessor() +{ + dispose(); + srs_freep(pprint); } -srs_error_t SrsPsRtpConn::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +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; - int keyframe = 0; 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) { - srs_trace("decode error"); - return srs_success; - //return srs_error_wrap(err, "decode"); + return srs_error_wrap(err, "ps rtp decode error"); } - if (pre_timestamp == -1) { - pre_timestamp = pkt.timestamp; - } + //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(); - //cache pkt payload by timestamp - if (cache_payload.find(pkt.timestamp) == cache_payload.end()) { - cache_payload[pkt.timestamp] = new SrsSimpleStream(); + 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(); } - - cache_payload[pkt.timestamp]->append(pkt.payload); - uint32_t cur_timestamp = pkt.timestamp; + //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; + // } - - if (pkt.marker) { + //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){ - if (cache_payload.find(pre_timestamp) != cache_payload.end()) { + }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; - cur_timestamp = pre_timestamp; } } - pre_timestamp = pkt.timestamp; - 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", - session_id.c_str(), nb_buf, pprint->age(), pkt.version, pkt.payload_type, pkt.sequence_number, pkt.timestamp, pkt.ssrc, + 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 - char *payload = cache_payload[cur_timestamp]->bytes(); - int payload_len = cache_payload[cur_timestamp]->length(); - on_ps_stream(payload, payload_len, cur_timestamp); - + //process completed frame data //clear processed one ps frame - map::iterator key = cache_payload.find(cur_timestamp); - if(key!=cache_payload.end()) + //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()) { - srs_freep(key->second); - cache_payload.erase(key); + 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; } -bool SrsPsRtpConn::can_send_ps_av_packet(){ +//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; @@ -267,11 +350,38 @@ bool SrsPsRtpConn::can_send_ps_av_packet(){ return false; } +int64_t SrsPsStreamDemixer::parse_ps_timestamp(const uint8_t* p) +{ + unsigned long b; + //total 33 bits + unsigned long val, val2, val3; -srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp) + //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; @@ -280,12 +390,11 @@ srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t time SrsSimpleStream audio_stream; uint64_t audio_pts = 0; uint64_t video_pts = 0; - int keyframe = 0; int pse_index = 0; #ifdef W_PS_FILE if (!ps_fw.is_open()) { - std::string filename = "test_ps_" + session_id + ".mpg"; + std::string filename = "test_ps_" + channel_id + ".mpg"; ps_fw.open(filename.c_str()); } ps_fw.write(ps_data, ps_size, NULL); @@ -345,19 +454,15 @@ srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t time && next_ps_pack[2] == (char)0x01 && next_ps_pack[3] == (char)0xE0) { - //pse - + //pse video stream SrsPsePacket* pse_pack = (SrsPsePacket*)next_ps_pack; unsigned char pts_dts_flags = (pse_pack->info[0] & 0xF0) >> 6; - int64_t pts = 0; + //in a frame of data, pts is obtained from the first PSE packet if (pse_index == 0 && pts_dts_flags > 0) { - pts = parse_ps_timestamp((unsigned char*)next_ps_pack + 9); - //srs_trace("vvvvvvvvvvvvvvvvvvvvvvv ts=%u pkt_ts=%u", pts, timestamp); + video_pts = parse_ps_timestamp((unsigned char*)next_ps_pack + 9); + srs_info("gb28181: ps stream video ts=%u pkt_ts=%u", pts, timestamp); } - - if (pse_index == 0) video_pts = pts; - pse_index +=1; int packlength = htons(pse_pack->length); @@ -370,7 +475,7 @@ srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t time #ifdef W_VIDEO_FILE if (!video_fw.is_open()) { - std::string filename = "test_video_" + session_id + ".h264"; + std::string filename = "test_video_" + channel_id + ".h264"; video_fw.open(filename.c_str()); } video_fw.write(next_ps_pack, payloadlen, NULL); @@ -379,9 +484,6 @@ srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t time next_ps_pack = next_ps_pack + payloadlen; complete_len = complete_len + payloadlen; incomplete_len = ps_size - complete_len; - - - //srs_trace("====================== V pts=%u", pts); } else if (next_ps_pack && next_ps_pack[0] == (char)0x00 @@ -413,8 +515,8 @@ srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t time 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_trace("aaaaaaaaaaaaaaaaaaaaaaaaaa ts=%u pkt_ts=%u", audio_pts, timestamp); - } + 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; @@ -424,7 +526,7 @@ srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t time #ifdef W_AUDIO_FILE if (!audio_fw.is_open()) { - std::string filename = "test_audio_" + session_id + ".aac"; + std::string filename = "test_audio_" + channel_id + ".aac"; audio_fw.open(filename.c_str()); } audio_fw.write(next_ps_pack, payload_len, NULL); @@ -434,63 +536,74 @@ srs_error_t SrsPsRtpConn::on_ps_stream(char* ps_data, int ps_size, uint32_t time complete_len = complete_len + (payload_len + 9 + pse_pack->stuffing_length); incomplete_len = ps_size - complete_len; - if (audio_enable && audio_stream.length() && can_send_ps_av_packet()) { - if ((err = gb28181->on_rtp_audio(&audio_stream, audio_pts)) != srs_success) { - srs_trace("process ps audio packet error %s", err); - //return srs_success; - //return srs_error_wrap(err, "process rtp packet"); + 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", - session_id.c_str(), next_ps_pack[0], next_ps_pack[1], next_ps_pack[2], next_ps_pack[3]); - //srs_trace(" ps ps_size=%d complete=%d h264len=%d\n", ps_size, complete_len, *h264length); + 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", - session_id.c_str(), ps_size, complete_len); - }else if (video_stream.length() && can_send_ps_av_packet()) { - if ((err = gb28181->on_rtp_video(&video_stream, video_pts, keyframe)) != srs_success) { - srs_trace("process ps video packet error"); - //return srs_success; - return srs_error_wrap(err, "process ps video packet error"); + 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; } -SrsGb28281ClientInfo::SrsGb28281ClientInfo() + +//Gb28181 Config +SrsGb28181Config::SrsGb28181Config(SrsConfDirective* c) { - req = new SrsSipRequest(); + // 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); } -SrsGb28281ClientInfo::~SrsGb28281ClientInfo() +SrsGb28181Config::~SrsGb28181Config() { - srs_freep(req); + } -SrsGb28181Conn::SrsGb28181Conn(SrsGb28181Caster* c, std::string id) +//SrsGb28181RtmpMuxer gb28181 rtmp muxer, process ps stream to rtmp +SrsGb28181RtmpMuxer::SrsGb28181RtmpMuxer(SrsGb28181Manger* c, std::string id, bool a, bool k) { - session_id = id; + channel_id = id; + gb28181_manger = c; + channel = new SrsGb28181StreamChannel(); - video_rtp = NULL; - audio_rtp = NULL; - - req = NULL; - caster = c; - info = new SrsGb28281ClientInfo; pprint = SrsPithyPrint::create_caster(); - - skt = new SrsStSocket(); - sip = new SrsSipStack(); - trd = new SrsSTCoroutine("gb28181conn", this); + trd = new SrsSTCoroutine("gb28181rtmpmuxer", this); sdk = NULL; vjitter = new SrsRtspJitter(); @@ -499,184 +612,197 @@ SrsGb28181Conn::SrsGb28181Conn(SrsGb28181Caster* c, std::string id) 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; - reg_expires = 3600; - register_time = 0; - alive_time = 0; - invite_time = 0; - register_status = Srs28181Unkonw; - alive_status = Srs28181Unkonw; - invite_status = Srs28181Unkonw; + stream_idle_timeout = -1; + recv_stream_time = 0; + + _rtmp_url = ""; } -SrsGb28181Conn::~SrsGb28181Conn() +SrsGb28181RtmpMuxer::~SrsGb28181RtmpMuxer() { close(); - - srs_freep(info); - - srs_freep(video_rtp); - srs_freep(audio_rtp); - + destroy(); + srs_cond_destroy(wait_ps_queue); + + srs_freep(channel); + srs_freep(ps_demixer); srs_freep(trd); - srs_freep(skt); - srs_freep(sip); - srs_freep(sdk); - srs_freep(req); - srs_freep(vjitter); srs_freep(ajitter); - srs_freep(pprint); } -srs_error_t SrsGb28181Conn::serve() +srs_error_t SrsGb28181RtmpMuxer::serve() { srs_error_t err = srs_success; if ((err = trd->start()) != srs_success) { - return srs_error_wrap(err, "gb28181 connection"); + return srs_error_wrap(err, "gb28181rtmpmuxer"); } return err; } -std::string SrsGb28181Conn::remote_ip() +std::string SrsGb28181RtmpMuxer::remote_ip() { return ""; } -void SrsGb28181Conn::set_request_info(SrsSipRequest *req) +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() { - srs_assert(req != NULL); - info->req->copy(req); + return channel->get_rtp_peer_ip(); } -std::string SrsGb28181Conn::get_session_id() +void SrsGb28181RtmpMuxer::set_rtmp_url(std::string url) +{ + _rtmp_url = url; +} +std::string SrsGb28181RtmpMuxer::rtmp_url() { - return session_id; + return _rtmp_url; } -srs_error_t SrsGb28181Conn::do_cycle() + +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; - - // consume all sip messages. + 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, "gb28181conn cycle"); + return srs_error_wrap(err, "gb28181 rtmp muxer cycle"); } - srs_utime_t now = srs_get_system_time(); - SrsGb28181Config config = caster->GetGb28181Config(); - - srs_utime_t reg_duration = 0; - srs_utime_t invite_duration = 0; - srs_utime_t alive_duration = 0; - srs_utime_t recv_rtp_duration = 0; - - if (register_status == Srs28181RegisterOk && register_time > 0) { - reg_duration = (now - register_time) / (1000*1000); - if (reg_duration > reg_expires) { - register_status = Srs28181Unkonw; - alive_status = Srs28181Unkonw; - invite_status = Srs28181Unkonw; - stop_rtp_listen(); + //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 (alive_status == Srs28181AliveOk && alive_time > 0){ - alive_duration = (now - alive_time) / (1000*1000); - if (alive_duration > config.sip_keepalive_timeout) { - srs_trace("gb28181: client id=%s alive timeout, remove conn", session_id.c_str()); - break; - } - } - - if (invite_status && invite_time > 0) { - invite_duration = (now - invite_time) / (1000*1000); - if (invite_status == Srs28181Trying && invite_duration > config.sip_ack_timeout) { - invite_status = Srs28181Unkonw; - stop_rtp_listen(); - } - recv_rtp_duration = (now - recv_rtp_time) / (1000*1000); - if (recv_rtp_duration > config.rtp_idle_timeout) { - invite_status = Srs28181Unkonw; - stop_rtp_listen(); - } + 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) / (1000*1000); + + 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; } - - if (pprint->can_print()) { - srs_trace("gb28181: client id=%s, druation reg=%u alive=%u invite=%u", - session_id.c_str(), reg_duration, alive_duration, invite_duration); - srs_trace("gb28181: client id=%s, status reg_status=%u alive_status=%u invite_status=%u", - session_id.c_str(), register_status, alive_status, invite_status); - } - - srs_usleep(1000 * 1000); + srs_cond_timedwait(wait_ps_queue, 5 * SRS_UTIME_MILLISECONDS); + //srs_usleep(1000 * 5); } return err; } -srs_error_t SrsGb28181Conn::start_rtp_listen(int port) -{ - srs_error_t err = srs_success; - - SrsPsRtpConn* rtp = NULL; - srs_freep(video_rtp); - SrsGb28181Config config = caster->GetGb28181Config(); - rtp = video_rtp = new SrsPsRtpConn(this, port, session_id, - config.audio_enable, config.wait_keyframe); - - if ((err = rtp->listen()) != srs_success) { - return srs_error_wrap(err, "rtp listen"); - } - srs_trace("gb28181conn: start rtp ps stream over server-port=%d", port); - return err; - -} - -srs_error_t SrsGb28181Conn::stop_rtp_listen() +void SrsGb28181RtmpMuxer::stop() { - srs_error_t err = srs_success; - - if (video_rtp) { - caster->free_port(video_rtp->port(), video_rtp->port() + 1); - srs_freep(video_rtp); - } - - if (audio_rtp) { - caster->free_port(audio_rtp->port(), audio_rtp->port() + 1); - srs_freep(audio_rtp); + if (trd){ + trd->interrupt(); } - //stop rtmp publish close(); - - return err; } +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 SrsGb28181Conn::cycle() +srs_error_t SrsGb28181RtmpMuxer::cycle() { - // serve the sip client. + // serve the rtmp muxer. srs_error_t err = do_cycle(); - stop_rtp_listen(); + gb28181_manger->stop_rtp_listen(channel_id); - caster->remove(this); - srs_trace("gb28181conn: client id=%d conn is remove", session_id.c_str()); + 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."); @@ -688,27 +814,31 @@ srs_error_t SrsGb28181Conn::cycle() return err; } -srs_error_t SrsGb28181Conn::on_rtp_video(SrsSimpleStream *stream, int64_t fpts, int keyframe) +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(); - recv_rtp_time = srs_get_system_time(); - - //srs_trace("==========================================VVV pts=%u", dts); SrsBuffer *avs = new SrsBuffer(stream->bytes(), stream->length()); SrsAutoFree(SrsBuffer, avs); // send each frame. @@ -769,7 +899,6 @@ srs_error_t SrsGb28181Conn::on_rtp_video(SrsSimpleStream *stream, int64_t fpts, } // ibp frame. - // TODO: FIXME: we should group all frames to a rtmp/flv message from one ts message. 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"); @@ -779,29 +908,32 @@ srs_error_t SrsGb28181Conn::on_rtp_video(SrsSimpleStream *stream, int64_t fpts, return err; } -srs_error_t SrsGb28181Conn::on_rtp_audio(SrsSimpleStream* stream, int64_t fdts) +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"); } - // sip tbn is ts tbn. if ((err = ajitter->correct(fdts)) != srs_success) { return srs_error_wrap(err, "jitter"); } - recv_rtp_time = srs_get_system_time(); + recv_stream_time = srs_get_system_time(); - // ts tbn to flv tbn. uint32_t dts = (uint32_t)(fdts / 90); - // send each frame. - SrsBuffer *avs = new SrsBuffer(stream->bytes(), stream->length()); - SrsAutoFree(SrsBuffer, avs); - if (!avs->empty()) { + // 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; @@ -867,7 +999,7 @@ srs_error_t SrsGb28181Conn::on_rtp_audio(SrsSimpleStream* stream, int64_t fdts) return err; } -srs_error_t SrsGb28181Conn::write_h264_sps_pps(uint32_t dts, uint32_t pts) +srs_error_t SrsGb28181RtmpMuxer::write_h264_sps_pps(uint32_t dts, uint32_t pts) { srs_error_t err = srs_success; @@ -904,7 +1036,7 @@ srs_error_t SrsGb28181Conn::write_h264_sps_pps(uint32_t dts, uint32_t pts) return err; } -srs_error_t SrsGb28181Conn::write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts) +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; @@ -936,7 +1068,7 @@ srs_error_t SrsGb28181Conn::write_h264_ipb_frame(char* frame, int frame_size, ui return rtmp_write_packet(SrsFrameTypeVideo, timestamp, flv, nb_flv); } -srs_error_t SrsGb28181Conn::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts) +srs_error_t SrsGb28181RtmpMuxer::write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts) { srs_error_t err = srs_success; @@ -949,7 +1081,7 @@ srs_error_t SrsGb28181Conn::write_audio_raw_frame(char* frame, int frame_size, S return rtmp_write_packet(SrsFrameTypeAudio, dts, data, size); } -srs_error_t SrsGb28181Conn::rtmp_write_packet(char type, uint32_t timestamp, char* data, int size) +srs_error_t SrsGb28181RtmpMuxer::rtmp_write_packet(char type, uint32_t timestamp, char* data, int size) { srs_error_t err = srs_success; @@ -973,7 +1105,7 @@ srs_error_t SrsGb28181Conn::rtmp_write_packet(char type, uint32_t timestamp, cha return err; } -srs_error_t SrsGb28181Conn::connect() +srs_error_t SrsGb28181RtmpMuxer::connect() { srs_error_t err = srs_success; @@ -983,19 +1115,8 @@ srs_error_t SrsGb28181Conn::connect() } // generate rtmp url to connect to. - std::string url; - if (true) { - std::string schema, host, vhost, app, param; - int port; - //srs_discovery_tc_url(rtmp_url, schema, host, vhost, app, rtsp_stream, port, param); - - // generate output by template. - std::string output = rtmp_url; - output = srs_string_replace(output, "[app]", "live"); - output = srs_string_replace(output, "[stream]", session_id); - output = srs_path_build_timestamp(output); - url = output; - } + std::string url = _rtmp_url; + // connect host. srs_utime_t cto = SRS_CONSTS_RTMP_TIMEOUT; srs_utime_t sto = SRS_CONSTS_RTMP_PULSE; @@ -1016,82 +1137,102 @@ srs_error_t SrsGb28181Conn::connect() return err; } -void SrsGb28181Conn::close() +void SrsGb28181RtmpMuxer::close() { srs_freep(sdk); } -SrsGb28181Config::SrsGb28181Config(SrsConfDirective* c) +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() { - // TODO: FIXME: support reload. - output = _srs_config->get_stream_caster_output(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_ide_timeout(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); - listen_port = _srs_config->get_stream_caster_listen(c); - sip_host = _srs_config->get_stream_caster_gb28181_host(c); - sip_realm = _srs_config->get_stream_caster_gb28181_realm(c); - sip_serial = _srs_config->get_stream_caster_gb28181_serial(c); - audio_enable = _srs_config->get_stream_caster_gb28181_audio_enable(c); - print_sip_message = _srs_config->get_stream_caster_gb28181_print_sip_message(c); - wait_keyframe = _srs_config->get_stream_caster_gb28181_wait_keyframe(c); + } -SrsGb28181Config::~SrsGb28181Config() +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())); } -//gb28181 caster -SrsGb28181Caster::SrsGb28181Caster(SrsConfDirective* c) + +//Global Singleton instance, init in SrsServer +SrsGb28181Manger* _srs_gb28181 = NULL; + +//SrsGb28181Manger +SrsGb28181Manger::SrsGb28181Manger(SrsConfDirective* c) { // TODO: FIXME: support reload. - //output = _srs_config->get_stream_caster_output(c); - //local_port_min = _srs_config->get_stream_caster_rtp_port_min(c); - //local_port_max = _srs_config->get_stream_caster_rtp_port_max(c); - sip = new SrsSipStack(); - manager = new SrsCoroutineManager(); config = new SrsGb28181Config(c); - lfd = NULL; + manager = new SrsCoroutineManager(); } -SrsGb28181Caster::~SrsGb28181Caster() +SrsGb28181Manger::~SrsGb28181Manger() { used_ports.clear(); srs_freep(manager); - srs_freep(sip); srs_freep(config); destroy(); } -srs_error_t SrsGb28181Caster::initialize() +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; } -void SrsGb28181Caster::set_stfd(srs_netfd_t fd) -{ - lfd = fd; -} - -SrsGb28181Config SrsGb28181Caster::GetGb28181Config() +SrsGb28181Config SrsGb28181Manger::get_gb28181_config() { return *config; } -srs_error_t SrsGb28181Caster::alloc_port(int* pport) +void SrsGb28181Manger::alloc_port(int* pport) { - srs_error_t err = srs_success; - // use a pair of port. for (int i = config->rtp_port_min; i < config->rtp_port_max - 1; i += 2) { if (!used_ports[i]) { @@ -1102,11 +1243,9 @@ srs_error_t SrsGb28181Caster::alloc_port(int* pport) } } srs_info("gb28181: alloc port=%d-%d", *pport, *pport + 1); - - return err; } -void SrsGb28181Caster::free_port(int lpmin, int lpmax) +void SrsGb28181Manger::free_port(int lpmin, int lpmax) { for (int i = lpmin; i < lpmax; i++) { used_ports[i] = false; @@ -1114,326 +1253,395 @@ void SrsGb28181Caster::free_port(int lpmin, int lpmax) srs_trace("gb28181: free rtp port=%d-%d", lpmin, lpmax); } -srs_error_t SrsGb28181Caster::on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf) +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) { - 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"); + 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; } - std::string peer_ip = std::string(address_string); - int peer_port = atoi(port_string); - // append to buffer. - //buffer->append(buf, nb_buf); - srs_error_t err = on_udp_bytes(peer_ip, peer_port, buf, nb_buf, (sockaddr*)from, fromlen); - if (err != srs_success) { - return srs_error_wrap(err, "process udp"); + 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; } -srs_error_t SrsGb28181Caster::on_udp_bytes(string peer_ip, int peer_port, - char* buf, int nb_buf, sockaddr* from, const int fromlen) +SrsGb28181RtmpMuxer* SrsGb28181Manger::fetch_rtmpmuxer(std::string id) { - srs_error_t err = srs_success; + SrsGb28181RtmpMuxer* muxer = NULL; - 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 (rtmpmuxers.find(id) == rtmpmuxers.end()) { + return NULL; } - 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, "recv message"); - } + muxer = rtmpmuxers[id]; + return muxer; +} - 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()); +SrsGb28181RtmpMuxer* SrsGb28181Manger::fetch_rtmpmuxer_by_ssrc(uint32_t ssrc) +{ + SrsGb28181RtmpMuxer* muxer = NULL; + if (rtmpmuxers_ssrc.find(ssrc) == rtmpmuxers_ssrc.end()) { + return NULL; } - req->peer_ip = peer_ip; - req->peer_port = peer_port; - - SrsAutoFree(SrsSipRequest, req); - - 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 srs_success; - } + muxer = rtmpmuxers_ssrc[ssrc]; + return muxer; +} - 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()); +void SrsGb28181Manger::rtmpmuxer_map_by_ssrc(SrsGb28181RtmpMuxer*muxer, uint32_t ssrc) +{ + if (rtmpmuxers_ssrc.find(ssrc) == rtmpmuxers_ssrc.end()) { + rtmpmuxers_ssrc[ssrc] = muxer; + } +} - SrsGb28181Conn* conn = NULL; +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); + } +} - if ((err = fetch_or_create(req, &conn)) != srs_success) { - srs_trace("gb28181: conn create faild:%s", req->uri.c_str()); - return srs_error_wrap(err, "conn create faild");; - } - srs_assert(conn != NULL); - - // if (conn->register_status == Srs28181Unkonw) - // { - // }else{ - // srs_trace("gb28181: %s client is register", req->sip_auth_id.c_str()); - // } +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(); +} - send_status(req, from, fromlen); - conn->register_status = Srs28181RegisterOk; - conn->register_time = srs_get_system_time(); - conn->reg_expires = req->expires; - +void SrsGb28181Manger::remove(SrsGb28181RtmpMuxer* muxer) +{ + std::string id = muxer->get_channel_id(); - }else if (req->is_message()) { - SrsGb28181Conn* conn = fetch(req); - if (!conn){ - srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); - return srs_success; - } - - if (conn->register_status == Srs28181Unkonw) { - send_bye(req, from, fromlen); - srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); - return srs_success; - } - - send_status(req, from, fromlen); - conn->alive_status = Srs28181AliveOk; - conn->alive_time = srs_get_system_time(); + 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); +} - if (conn->register_status == Srs28181RegisterOk && - conn->alive_status == Srs28181AliveOk && - conn->invite_status != Srs28181InviteOk) - { - int lpm = 0; - if (alloc_port(&lpm) != srs_success) { - return srs_error_wrap(err, "alloc port"); - } - if (lpm){ - send_invite(req, from, fromlen, lpm); - conn->rtmp_url = config->output; - conn->start_rtp_listen(lpm); - conn->invite_status == Srs28181Trying; - conn->invite_time = srs_get_system_time(); - } +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"); + } - }else if (req->is_invite()) { - SrsGb28181Conn* conn = fetch(req); - - 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 (!conn){ - send_bye(req, from, fromlen); - srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); - return srs_success; + 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"); } - if (conn->register_status == Srs28181Unkonw || - conn->alive_status == Srs28181Unkonw) { - send_bye(req, from, fromlen); - srs_trace("gb28181: %s client not registered or not alive", req->sip_auth_id.c_str()); - return srs_success; - } - - 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); - conn->invite_status = Srs28181InviteOk; - conn->invite_time = srs_get_system_time(); - } - }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()); - - SrsGb28181Conn* conn = fetch(req); - send_status(req, from, fromlen); - - if (!conn){ - srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); - return srs_success; - } - - conn->stop_rtp_listen(); - conn->invite_status = Srs28181Bye; - conn->invite_time = 0; - - }else{ - srs_trace("gb28181: ingor request method=%s", req->method.c_str()); + srs_trace("gb28181: start rtp ps stream over server-port=%d", port); } - + return err; } -srs_error_t SrsGb28181Caster::send_message(sockaddr* from, int fromlen, std::stringstream& ss) +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; + } - std::string str = ss.str(); - if (config->print_sip_message) - srs_trace("gb28181: send_message:%s", str.c_str()); - srs_assert(!str.empty()); + SrsGb28181RtmpMuxer* muxer = it->second; + SrsGb28181StreamChannel sess = muxer->get_channel(); - int ret = srs_sendto(lfd, (char*)str.c_str(), (int)str.length(), from, fromlen, SRS_UTIME_NO_TIMEOUT); - if (ret <= 0){ - return srs_error_wrap(err, "gb28181: send_message falid"); + int port = sess.get_rtp_port(); + if (port == config->rtp_mux_port) { + return; } - - return err; + + 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); } -srs_error_t SrsGb28181Caster::send_bye(SrsSipRequest *req, sockaddr *f, int l) +//api +uint32_t SrsGb28181Manger::create_stream_channel(SrsGb28181StreamChannel *channel) { - srs_error_t err = srs_success; - srs_assert(req); + srs_assert(channel); - std::stringstream ss; - - req->host = config->sip_host; - req->host_port = config->listen_port; - req->realm = config->sip_realm; - req->serial = config->sip_serial; + std::string id = channel->get_channel_id(); + SrsGb28181RtmpMuxer *muxer = NULL; - sip->req_bye(ss, req); - send_message(f, l, ss); + muxer = fetch_rtmpmuxer(id); + if (muxer){ + SrsGb28181StreamChannel s = muxer->get_channel(); + channel->copy(&s); + //return ERROR_GB28181_SESSION_IS_EXIST; + return ERROR_SUCCESS; + } - return err; + if (channel->get_stream().empty()){ + channel->set_stream("[stream]"); + } -} -srs_error_t SrsGb28181Caster::send_ack(SrsSipRequest *req, sockaddr *f, int l) -{ - srs_error_t err = srs_success; - srs_assert(req); + if (channel->get_app().empty()){ + channel->set_stream("[app]"); + } - std::stringstream ss; - - req->host = config->sip_host; - req->host_port = config->listen_port; - req->realm = config->sip_realm; - req->serial = config->sip_serial; + 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; + } - sip->resp_ack(ss, req); - send_message(f, l, ss); + //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; + } - return err; -} + //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); -srs_error_t SrsGb28181Caster::send_invite(SrsSipRequest *req, sockaddr *f, int l, int port) -{ - srs_error_t err = srs_success; - srs_assert(req); + //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::stringstream ss; - - req->host = config->sip_host; - req->host_port = config->listen_port; - req->realm = config->sip_realm; - req->serial = config->sip_serial; + 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()); - sip->req_invite(ss, req, port); - send_message(f, l, ss); + //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); - return err; + muxer->copy_channel(channel); + return ERROR_SUCCESS; } -srs_error_t SrsGb28181Caster::send_status(SrsSipRequest *req, sockaddr *f, int l) +uint32_t SrsGb28181Manger::delete_stream_channel(std::string id) { - srs_error_t err = srs_success; - srs_assert(req); - - std::stringstream ss; - - req->host = config->sip_host; - req->host_port = config->listen_port; - req->realm = config->sip_realm; - req->serial = config->sip_serial; + //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; + } +} - sip->resp_status(ss, req); - send_message(f, l, ss); - return err; +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; } - -srs_error_t SrsGb28181Caster::fetch_or_create(SrsSipRequest* r, SrsGb28181Conn** gb28181) +uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc) { - srs_error_t err = srs_success; + 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; + } - SrsGb28181Conn* conn = NULL; - if ((conn = fetch(r)) != NULL) { - *gb28181 = conn; - return err; + 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(); } - - string key = r->sip_auth_id; - conn = new SrsGb28181Conn(this, key); - conn->set_request_info(r); - if ((err = conn->serve()) != srs_success) { - return srs_error_wrap(err, "sipconn serve %s", key.c_str()); - } - clients[key] = conn; - *gb28181 = conn; - - return err; + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_invite(&req, ip, port, ssrc); + } -SrsGb28181Conn* SrsGb28181Caster::fetch(const SrsSipRequest* r) +uint32_t SrsGb28181Manger::notify_sip_bye(std::string id) { - SrsGb28181Conn* conn = NULL; - - string key = r->sip_auth_id; - if (clients.find(key) == clients.end()) { - return NULL; + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; } - - conn = clients[key]; - return conn; -} + SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); + if (muxer){ + muxer->rtmp_close(); + } + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_bye(&req); +} -void SrsGb28181Caster::destroy() +uint32_t SrsGb28181Manger::notify_sip_raw_data(std::string id, std::string data) { - std::map::iterator it; - for (it = clients.begin(); it != clients.end(); ++it) { - SrsGb28181Conn* conn = it->second; - manager->remove(conn); + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; } - clients.clear(); + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_sip_raw_data(&req, data); + } -void SrsGb28181Caster::remove(SrsGb28181Conn* conn) +uint32_t SrsGb28181Manger::notify_sip_unregister(std::string id) { - std::string id = conn->get_session_id(); - map::iterator key = clients.find(id); - if (key != clients.end()) { - clients.erase(key); + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; } - manager->remove(conn); + + 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 index 3d8bd13b0..8eff26b32 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -38,16 +39,14 @@ #include #include #include +#include +#include + +#define RTP_PORT_MODE_FIXED "fixed" +#define RTP_PORT_MODE_RANDOM "random" -class SrsStSocket; -class SrsRtpConn; -class SrsRtspConn; -class SrsRtspStack; -class SrsRtspCaster; class SrsConfDirective; class SrsRtpPacket; -class SrsRequest; -class SrsStSocket; class SrsRtmpClient; class SrsRawH264Stream; class SrsRawAacStream; @@ -58,74 +57,134 @@ class SrsSimpleStream; class SrsPithyPrint; class SrsSimpleRtmpClient; class SrsSipStack; -class SrsGb28181Caster; +class SrsGb28181Manger; class SrsRtspJitter; -class SrsRtspAudioCache; class SrsSipRequest; -class SrsGb28181Conn; -class SrsGb28281ClientInfo; - -/* gb28181 program stream struct define +class SrsGb28181RtmpMuxer; +class SrsGb28181Config; +class SrsGb28181PsRtpProcessor; +class SrsGb28181SipService; +class SrsGb28181StreamChannel; -*/ - -struct SrsPsPacketStartCode +//ps rtp header packet parse +class SrsPsRtpPacket: public SrsRtpPacket { - uint8_t start_code[3]; - uint8_t stream_id[1]; +public: + SrsPsRtpPacket(); + virtual ~SrsPsRtpPacket(); +public: + virtual srs_error_t decode(SrsBuffer* stream); }; -struct SrsPsPacketHeader +//randomly assigned ports receive gb28281 device streams +class SrsPsRtpListener: public ISrsUdpHandler { - SrsPsPacketStartCode start;// 4 - uint8_t info[9]; - uint8_t stuffing_length; +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); }; -struct SrsPsPacketBBHeader +//multiplexing service, single port receiving all gb28281 device streams +class SrsGb28181RtpMuxService : public ISrsUdpHandler { - SrsPsPacketStartCode start; - uint16_t length; -}; +private: + SrsGb28181Config *config; + SrsGb28181PsRtpProcessor *rtp_processor; +public: + SrsGb28181RtpMuxService(SrsConfDirective* c); + virtual ~SrsGb28181RtpMuxService(); -struct SrsPsePacket -{ - SrsPsPacketStartCode start; - uint16_t length; - uint8_t info[2]; - uint8_t stuffing_length; + // Interface ISrsUdpHandler +public: + virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); }; -struct SrsPsMapPacket + +//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 { - SrsPsPacketStartCode start; - uint16_t length; +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); }; - - -class SrsPsRtpPacket: public SrsRtpPacket +//ps stream processing parsing interface +class ISrsPsStreamHander { public: - SrsPsRtpPacket(); - virtual ~SrsPsRtpPacket(); + ISrsPsStreamHander(); + virtual ~ISrsPsStreamHander(); public: - virtual srs_error_t decode(SrsBuffer* stream); + 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; }; -// A rtp connection which transport a stream. -class SrsPsRtpConn: public ISrsUdpHandler +//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 { -private: - SrsPithyPrint* pprint; - SrsUdpListener* listener; - SrsGb28181Conn* gb28181; - SrsPsRtpPacket* cache; - std::map cache_payload; - std::string session_id; - int _port; - uint32_t pre_timestamp; +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; @@ -133,117 +192,89 @@ private: bool first_keyframe_flag; bool wait_first_keyframe; bool audio_enable; - -public: - SrsPsRtpConn(SrsGb28181Conn* r, int p, std::string sid, bool a, bool k); - virtual ~SrsPsRtpConn(); - -private: - int64_t parse_ps_timestamp(const uint8_t* p); + 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(); - void dispose(); -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); - virtual srs_error_t on_ps_stream(char* ps_data, int ps_size, uint32_t timestamp); -}; - -class SrsGb28281ClientInfo { -public: - SrsGb28281ClientInfo(); - virtual ~SrsGb28281ClientInfo(); - public: - sockaddr* sock_from; - int sock_fromlen; - srs_netfd_t stfd; - SrsSipRequest *req; + 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); }; -enum Srs28181CtrlStatusType{ - Srs28181Unkonw = 0, - Srs28181RegisterOk = 1, - Srs28181AliveOk = 2, - Srs28181InviteOk = 3, - Srs28181Trying = 4, - Srs28181Bye = 5, -}; -class SrsGb28181Conn : public ISrsCoroutineHandler, public ISrsConnection +//RTMP multiplexer, which processes the raw H264 / AAC, +//then publish it to RTMP server +class SrsGb28181RtmpMuxer : public ISrsCoroutineHandler, + public ISrsConnection, public ISrsPsStreamHander { private: - std::string output_template; SrsPithyPrint* pprint; -public: - Srs28181CtrlStatusType register_status; - Srs28181CtrlStatusType alive_status; - Srs28181CtrlStatusType invite_status; - srs_utime_t register_time; - srs_utime_t alive_time; - srs_utime_t invite_time; - srs_utime_t recv_rtp_time; - - std::string rtmp_url; - int reg_expires; - + SrsGb28181StreamChannel *channel; + int stream_idle_timeout; + srs_utime_t recv_stream_time; private: - std::string session_id; - // video stream. - int video_id; - std::string video_codec; - SrsPsRtpConn* video_rtp; - // audio stream. - int audio_id; - std::string audio_codec; + std::string channel_id; + std::string _rtmp_url; + std::string video_ssrc; + std::string audio_ssrc; int audio_sample_rate; int audio_channel; - SrsPsRtpConn* audio_rtp; -public: - SrsGb28281ClientInfo* info; -private: - SrsStSocket* skt; - SrsSipStack* sip; - SrsGb28181Caster* caster; + + SrsGb28181Manger* gb28181_manger; SrsCoroutine* trd; -private: - SrsSipRequest* req; + SrsPsStreamDemixer* ps_demixer; + srs_cond_t wait_ps_queue; + SrsSimpleRtmpClient* sdk; SrsRtspJitter* vjitter; SrsRtspJitter* ajitter; -private: + SrsRawH264Stream* avc; std::string h264_sps; std::string h264_pps; bool h264_sps_changed; bool h264_pps_changed; bool h264_sps_pps_sent; -private: + SrsRawAacStream* aac; std::string aac_specific_config; + +public: + std::queue ps_queue; + public: - SrsGb28181Conn(SrsGb28181Caster* c, std::string id); - virtual ~SrsGb28181Conn(); + SrsGb28181RtmpMuxer(SrsGb28181Manger* m, std::string id, bool a, bool k); + virtual ~SrsGb28181RtmpMuxer(); + public: virtual srs_error_t serve(); - virtual std::string remote_ip(); - virtual void set_request_info(SrsSipRequest *req); - virtual std::string get_session_id(); + 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(); -// internal methods -public: - virtual srs_error_t start_rtp_listen(int port); - virtual srs_error_t stop_rtp_listen(); + 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, int keyframe); + 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); @@ -255,75 +286,152 @@ private: 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 sip_host; - std::string sip_port; - std::string sip_serial; - std::string sip_realm; - int sip_ack_timeout; - int sip_keepalive_timeout; + std::string host; int rtp_idle_timeout; bool audio_enable; + bool wait_keyframe; std::string output; int rtp_port_min; int rtp_port_max; - int listen_port; + 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 wait_keyframe; + bool sip_auto_play; + bool sip_invite_port_fixed; + public: SrsGb28181Config(SrsConfDirective* c); virtual ~SrsGb28181Config(); }; -//gb28181 conn manager -class SrsGb28181Caster : public ISrsUdpHandler +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; - SrsSipStack *sip; - srs_netfd_t lfd; -private: - std::map clients; + 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: - SrsGb28181Caster(SrsConfDirective* c); - virtual ~SrsGb28181Caster(); + //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: - srs_error_t fetch_or_create(SrsSipRequest* r, SrsGb28181Conn** gb28181); - virtual SrsGb28181Conn* fetch(const SrsSipRequest* r); - virtual void destroy(); + void destroy(); + public: // Alloc a rtp port from local ports pool. // @param pport output the rtp port. - virtual srs_error_t alloc_port(int* pport); + void alloc_port(int* pport); // Free the alloced rtp port. - virtual void free_port(int lpmin, int lpmax); - virtual srs_error_t initialize(); + void free_port(int lpmin, int lpmax); + srs_error_t initialize(); - virtual void set_stfd(srs_netfd_t fd); - virtual SrsGb28181Config GetGb28181Config(); + SrsGb28181Config get_gb28181_config(); + srs_error_t start_ps_rtp_listen(std::string id, int port); + void stop_rtp_listen(std::string id); -// Interface ISrsUdpHandler -public: - virtual srs_error_t on_udp_packet(const sockaddr* from, const int fromlen, char* buf, int nb_buf); -private: - virtual srs_error_t on_udp_bytes(std::string host, int port, char* buf, int nb_buf, sockaddr* from, int fromlen); -// internal methods. public: - virtual srs_error_t send_message(sockaddr* f, int l, std::stringstream& ss); - virtual srs_error_t send_bye(SrsSipRequest *req, sockaddr *f, int l); - virtual srs_error_t send_ack(SrsSipRequest *req, sockaddr *f, int l); - virtual srs_error_t send_invite(SrsSipRequest *req, sockaddr *f, int l, int port); - virtual srs_error_t send_status(SrsSipRequest *req, sockaddr *f, int l); - virtual void remove(SrsGb28181Conn* conn); + 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..64a8512b6 --- /dev/null +++ b/trunk/src/app/srs_app_gb28181_sip.cpp @@ -0,0 +1,471 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * 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..17e4313cb --- /dev/null +++ b/trunk/src/app/srs_app_gb28181_sip.hpp @@ -0,0 +1,161 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Winlin + * + * 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 5355068eb..d2174d08b 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1301,6 +1301,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 5957ff2f3..7713d9816 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -210,6 +210,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_server.cpp b/trunk/src/app/srs_app_server.cpp index 26106feaa..b00f68237 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -52,6 +52,7 @@ using namespace std; #include #include #include +#include // system interval in srs_utime_t, // all resolution times should be times togother, @@ -109,8 +110,10 @@ std::string srs_listener_type2string(SrsListenerType type) return "RTSP"; case SrsListenerFlv: return "HTTP-FLV"; - case SrsListenerGb28181: + case SrsListenerGb28181Sip: return "GB28181-SIP over UDP"; + case SrsListenerGb28181RtpMux: + return "GB28181-Stream over RTP"; default: return "UNKONWN"; } @@ -301,7 +304,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 || type == SrsListenerGb28181); + srs_assert(type == SrsListenerMpegTsOverUdp + || type == SrsListenerGb28181Sip + || type == SrsListenerGb28181RtpMux); ip = i; port = p; @@ -344,9 +349,13 @@ SrsGb28181Listener::SrsGb28181Listener(SrsServer* svr, SrsListenerType t, SrsCon { // the caller already ensure the type is ok, // we just assert here for unknown stream caster. - srs_assert(type == SrsListenerGb28181); - if (type == SrsListenerGb28181) { - caster = new SrsGb28181Caster(c); + srs_assert(type == SrsListenerGb28181Sip + ||type == SrsListenerGb28181RtpMux); + + if (type == SrsListenerGb28181Sip) { + caster = new SrsGb28181SipService(c); + }else if(type == SrsListenerGb28181RtpMux){ + caster = new SrsGb28181RtpMuxService(c); } } @@ -526,6 +535,9 @@ void SrsServer::destroy() srs_freep(signal_manager); srs_freep(conn_manager); + + //free global gb28281 manager + srs_freep(_srs_gb28181); } void SrsServer::dispose() @@ -771,6 +783,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) { @@ -1088,6 +1104,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; @@ -1113,19 +1153,33 @@ srs_error_t SrsServer::listen_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)) { - listener = new SrsGb28181Listener(this, SrsListenerGb28181, stream_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 46127b6c6..4a7fba927 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,8 +73,10 @@ enum SrsListenerType SrsListenerRtsp = 4, // TCP stream, FLV stream over HTTP. SrsListenerFlv = 5, - // UDP stream, gb28181 stream - SrsListenerGb28181 = 6, + // UDP stream, gb28181 ps stream over rtp, + SrsListenerGb28181RtpMux = 6, + // UDP gb28181 sip server + SrsListenerGb28181Sip = 7, }; // A common tcp listener, for RTMP/HTTP server. @@ -158,7 +164,7 @@ public: virtual ~SrsUdpCasterListener(); }; -// A UDP sip listener, for sip server. +// A UDP gb28181 listener, for sip and rtp stream mux server. class SrsGb28181Listener : public SrsUdpStreamListener { public: @@ -290,6 +296,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 f7375780a..9180dfd54 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -322,6 +322,22 @@ #define ERROR_BASE64_DECODE 4039 #define ERROR_HTTP_STREAM_EOF 4040 +/////////////////////////////////////////////////////// +// 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 index e3031fd20..1b5d49812 100644 --- a/trunk/src/protocol/srs_sip_stack.cpp +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -43,8 +43,6 @@ using namespace std; #include #include -#define SIP_MAX_HEADER_LEN 2049 - unsigned int srs_sip_random(int min,int max) { srand(int(time(0))); @@ -68,10 +66,34 @@ std::string srs_sip_get_form_to_uri(std::string msg) return msg; } - msg = msg.substr(0, pos2-1); + 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) { @@ -120,6 +142,8 @@ SrsSipRequest::SrsSipRequest() status = ""; expires = 3600; max_forwards = 70; + www_authenticate = ""; + authorization = ""; cmdtype = SrsSipCmdRequest; host = "127.0.0.1";; @@ -202,6 +226,8 @@ void SrsSipRequest::copy(SrsSipRequest* src) 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; @@ -215,10 +241,8 @@ void SrsSipRequest::copy(SrsSipRequest* src) sip_username = src->sip_username; peer_ip = src->peer_ip; peer_port = src->peer_port; - } - SrsSipStack::SrsSipStack() { buf = new SrsSimpleStream(); @@ -256,8 +280,8 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m body = header_body.at(1); } - //srs_trace("sip: header=%s\n", header.c_str()); - //srs_trace("sip: body=%s\n", body.c_str()); + 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(); @@ -274,7 +298,7 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m if (firstline == ""){ firstline = oneline; - //srs_trace("=== first line=%s", firstline.c_str()); + srs_info("sip: first line=%s", firstline.c_str()); }else{ size_t pos = oneline.find(":"); if (pos != string::npos){ @@ -322,6 +346,7 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m } } 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"); } @@ -333,13 +358,19 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m } 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()); } } } - //srs_trace("====new line=%s", oneline.c_str()); } }else{ p++; @@ -370,24 +401,24 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m req->sip_username = req->sip_auth_id; - //srs_trace("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_trace("via=%s", req->via.c_str()); - // srs_trace("via_branch=%s", req->branch.c_str()); - //srs_trace("cseq=%d", req->seq); - // srs_trace("contact=%s", req->contact.c_str()); - //srs_trace("from=%s", req->from.c_str()); - //srs_trace("to=%s", req->to.c_str()); - //srs_trace("callid=%s", req->call_id.c_str()); - // srs_trace("status=%s", req->status.c_str()); - // srs_trace("from_tag=%s", req->from_tag.c_str()); - // srs_trace("to_tag=%s", req->to_tag.c_str()); - //srs_trace("sip_auth_id=%s", req->sip_auth_id.c_str()); + 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; } -srs_error_t SrsSipStack::resp_keepalive(std::stringstream& ss, SrsSipRequest *req){ +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 @@ -398,30 +429,43 @@ srs_error_t SrsSipStack::resp_keepalive(std::stringstream& ss, SrsSipRequest *re << "Max-Forwards: 70" << SRS_RTSP_CRLF << "User-Agent: "<< SRS_SIP_USER_AGENT << SRS_RTSP_CRLF << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; - - return srs_success; -} - -srs_error_t SrsSipStack::resp_ack(std::stringstream& ss, SrsSipRequest *req){ - - 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 << ";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 << " " << req->method << SRS_RTSP_CRLF - << "Max-Forwards: 70" << SRS_RTSP_CRLF - << "User-Agent: "<< SRS_SIP_USER_AGENT << SRS_RTSP_CRLF - << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; - - return srs_success; } -srs_error_t SrsSipStack::resp_status(stringstream& ss, SrsSipRequest *req) +void SrsSipStack::resp_status(stringstream& ss, SrsSipRequest *req) { - srs_error_t err = srs_success; - 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 @@ -430,8 +474,43 @@ srs_error_t SrsSipStack::resp_status(stringstream& ss, SrsSipRequest *req) << "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 @@ -442,61 +521,104 @@ srs_error_t SrsSipStack::resp_status(stringstream& ss, SrsSipRequest *req) << "Content-Length: 0" << SRS_RTSP_CRLFCRLF; } - return err; } -srs_error_t SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, int port) +void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, int port, uint32_t ssrc) { - /* - INVITE sip:34020000001320000001@3402000000 SIP/2.0 - Via: SIP/2.0/UDP 192.168.1.22:15060;rport;branch=z9hG4bK369961166 - From: ;tag=536961166 - To: - Call-ID: 929961057 - CSeq: 3 INVITE - Content-Type: APPLICATION/SDP - Contact: - Max-Forwards: 70 - User-Agent: XXXXXXX XXXXXXX - Subject: 34020000001320000001:0200000001,34020000002020000001:0 - Content-Length: 247 + /* + //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=34020000002000000001 0 0 IN IP4 192.168.1.23 + o=34020000001320000003 0 0 IN IP4 39.100.155.146 s=Play - c=IN IP4 192.168.1.23 + c=IN IP4 39.100.155.146 t=0 0 - m=video 30000 RTP/AVP 96 97 98 99 + m=video 9000 RTP/AVP 96 a=recvonly a=rtpmap:96 PS/90000 - a=rtpmap:97 MPEG4/90000 - a=rtpmap:98 H264/90000 - a=rtpmap:99 H265/90000 - y=0200000001 + 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 */ - srs_error_t err = srs_success; - int ssrc = srs_sip_random(10000, 99999); std::stringstream sdp; sdp << "v=0" << SRS_RTSP_CRLF - << "o=" << req->sip_auth_id << " 0 0 IN IP4 " << req->host << SRS_RTSP_CRLF + << "o=" << req->sip_auth_id << " 0 0 IN IP4 " << ip << SRS_RTSP_CRLF << "s=Play" << SRS_RTSP_CRLF - << "c=IN IP4 " << req->host << SRS_RTSP_CRLF + << "c=IN IP4 " << ip << SRS_RTSP_CRLF << "t=0 0" << SRS_RTSP_CRLF - << "m=video " << port <<" RTP/AVP 96 97 98 99" << 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 - << "a=rtpmap:97 MPEG4/90000" << SRS_RTSP_CRLF - << "a=rtpmap:98 H264/90000" << SRS_RTSP_CRLF - << "a=rtpmap:99 H265/90000" << SRS_RTSP_CRLF - << "y=00181" << ssrc << 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; + 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 @@ -507,9 +629,14 @@ srs_error_t SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, int po 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=z9hG4bK3420" << rand << SRS_RTSP_CRLF - << "From: from << ">;tag=51235" << rand << 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 << ":00181" << ssrc << "," << req->serial << ":0" << SRS_RTSP_CRLF + << "Subject: "<< req->sip_auth_id << ":" << ssrc << "," << req->serial << ":0" << SRS_RTSP_CRLF << "Content-Length: " << sdp.str().length() << SRS_RTSP_CRLFCRLF << sdp.str(); +} - return err; + +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; } -srs_error_t SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) +void SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) { - srs_error_t err = srs_success; + /* + //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->host << ":" << req->host_port; - to << 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(); - int rand = srs_sip_random(1000, 9999); + 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 << ";branch=z9hG4bK3420" << rand << SRS_RTSP_CRLF - << "From: from << ">;tag=51235" << rand << SRS_RTSP_CRLF - << "To: to << ">" << SRS_RTSP_CRLF - << "Call-ID: 20000" << rand << SRS_RTSP_CRLF - << "CSeq: 21 BYE" << 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; - return err; } - #endif diff --git a/trunk/src/protocol/srs_sip_stack.hpp b/trunk/src/protocol/srs_sip_stack.hpp index f4868a724..26f712081 100644 --- a/trunk/src/protocol/srs_sip_stack.hpp +++ b/trunk/src/protocol/srs_sip_stack.hpp @@ -83,6 +83,9 @@ public: long expires; int max_forwards; + std::string www_authenticate; + std::string authorization; + public: std::string serial; std::string realm; @@ -128,12 +131,13 @@ protected: virtual srs_error_t do_parse_request(SrsSipRequest* req, const char *recv_msg); public: - virtual srs_error_t resp_status(std::stringstream& ss, SrsSipRequest *req); - virtual srs_error_t resp_keepalive(std::stringstream& ss, SrsSipRequest *req); - virtual srs_error_t resp_ack(std::stringstream& ss, SrsSipRequest *req); + 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 srs_error_t req_invite(std::stringstream& ss, SrsSipRequest *req, int port); - virtual srs_error_t req_bye(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); }; From c7113f27e0b4474607e7fff29573a591bcd46192 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 09:35:36 +0800 Subject: [PATCH 04/13] Play stream by WebRTC on iOS/Android/PC browser. 4.0.17 --- README.md | 1 + trunk/src/core/srs_core_version4.hpp | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d4481cdb7..59814182d 100755 --- a/README.md +++ b/README.md @@ -157,6 +157,7 @@ For previous versions, please read: ## V4 changes +* v4.0, 2020-03-31, Play stream by WebRTC on iOS/Android/PC browser. 4.0.17 * v4.0, 2020-03-28, Support multiple OS/Platform build cache. 4.0.16 * v4.0, 2020-03-28, For [#1250][bug #1250], support macOS, OSX, MacbookPro, Apple Darwin. * v4.0, 2020-03-22, Welcome maintainers [Runner365](https://github.com/runner365), [John](https://github.com/xiaozhihong) and [B.P.Y(Bepartofyou)](https://github.com/Bepartofyou). 4.0.15 diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 5b70e84e6..210565f1e 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -24,6 +24,6 @@ #ifndef SRS_CORE_VERSION4_HPP #define SRS_CORE_VERSION4_HPP -#define SRS_VERSION4_REVISION 16 +#define SRS_VERSION4_REVISION 17 #endif From c8aaa5a1a6a55d4e9f3216ac985e912e83cbb528 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 16:40:08 +0800 Subject: [PATCH 05/13] Update circle CI script --- .circleci/config.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 741270d5d..1a44c21fb 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,13 +5,17 @@ jobs: - image: ossrs/srs:dev steps: - checkout - - run: cd trunk && ./configure --without-rtc && make && ./configure --with-utest && make + - run: | + cd trunk && ./configure --without-rtc --without-utest && make && + ./configure --with-rtc --with-utest && make clean && make test: docker: - image: ossrs/srs:dev steps: - checkout - - run: cd trunk && ./configure --with-utest --gcov && make && ./objs/srs_utest && bash auto/coverage.sh + - run: | + cd trunk && ./configure --with-utest --gcov && make && + ./objs/srs_utest && bash auto/coverage.sh workflows: version: 2 build_and_test: From ecf0f012b5d6e78fd69cd4482d443efb4b7083ff Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 17:26:32 +0800 Subject: [PATCH 06/13] Add comments for RTC candidate. --- trunk/conf/full.conf | 12 ++++++++---- trunk/conf/rtc.conf | 5 ++--- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index bf2ec8377..001f27a9b 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -316,11 +316,15 @@ rtc_server { # The udp listen port, we will reuse it for connections. # default: 8080 listen 8000; - # The exposed candidate IP, response in SDP candidate line. - # It can be: - # * Retrieve server IP automatically, specified by stats.network for multiple networks. - # $CANDIDATE Read the IP from ENV variable $EIP, see https://github.com/ossrs/srs/issues/307#issuecomment-599028124 + # The exposed candidate IPs, response in SDP candidate line. It can be: + # * Retrieve server IP automatically, from all network interfaces. + # eth0 Retrieve server IP by specified network interface name. # TODO: Implements it. + # $CANDIDATE Read the IP from ENV variable $EIP, use * if not set, see https://github.com/ossrs/srs/issues/307#issuecomment-599028124 # x.x.x.x A specified IP address or DNS name, which can be access by client such as Chrome. + # You can specific more than one interface name: + # eth0 eth1 Use network interface eth0 and eth1. # TODO: Implements it. + # Also by IP or DNS names: + # 192.168.1.3 10.1.2.3 rtc.me # TODO: Implements it. # default: * candidate *; } diff --git a/trunk/conf/rtc.conf b/trunk/conf/rtc.conf index f4578cec5..608ba3798 100644 --- a/trunk/conf/rtc.conf +++ b/trunk/conf/rtc.conf @@ -23,10 +23,9 @@ rtc_server { # Listen at udp://8000 listen 8000; # - # The $CANDIDATE means fetch from env, if not configed, use default * as bellow. + # The $CANDIDATE means fetch from env, if not configed, use * as default. # - # The * means using IP of network interface stats.network, - # For example, if stats.network=0, then use IP of eth0 as candidate. + # The * means retrieving server IP automatically, from all network interfaces, # @see https://github.com/ossrs/srs/issues/307#issuecomment-599028124 candidate $CANDIDATE; } From 6022ac5cdb595c8fe7f6ed89f4b74bfed243432e Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 17:33:04 +0800 Subject: [PATCH 07/13] Add comments for RTC candidate. --- trunk/conf/full.conf | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 001f27a9b..2dcd3aa52 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -325,6 +325,8 @@ rtc_server { # eth0 eth1 Use network interface eth0 and eth1. # TODO: Implements it. # Also by IP or DNS names: # 192.168.1.3 10.1.2.3 rtc.me # TODO: Implements it. + # And by multiple ENV variables: + # $CANDIDATE $EIP # TODO: Implements it. # default: * candidate *; } From 45db63249d393a142dfeb44c36911d2b71f8b047 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 18:03:04 +0800 Subject: [PATCH 08/13] Update author to John for RTC --- trunk/src/app/srs_app_rtc.cpp | 2 +- trunk/src/app/srs_app_rtc.hpp | 2 +- trunk/src/app/srs_app_rtc_conn.cpp | 2 +- trunk/src/app/srs_app_rtc_conn.hpp | 2 +- trunk/src/app/srs_app_sdp.cpp | 2 +- trunk/src/app/srs_app_sdp.hpp | 2 +- trunk/src/protocol/srs_stun_stack.cpp | 23 +++++++++++++++++++++++ trunk/src/protocol/srs_stun_stack.hpp | 5 +++-- 8 files changed, 32 insertions(+), 8 deletions(-) diff --git a/trunk/src/app/srs_app_rtc.cpp b/trunk/src/app/srs_app_rtc.cpp index 0d774a003..bc303c932 100644 --- a/trunk/src/app/srs_app_rtc.cpp +++ b/trunk/src/app/srs_app_rtc.cpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 John * * 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 diff --git a/trunk/src/app/srs_app_rtc.hpp b/trunk/src/app/srs_app_rtc.hpp index e763ca550..9cb174abf 100644 --- a/trunk/src/app/srs_app_rtc.hpp +++ b/trunk/src/app/srs_app_rtc.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 John * * 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 diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp index 07a927394..2598cc33e 100644 --- a/trunk/src/app/srs_app_rtc_conn.cpp +++ b/trunk/src/app/srs_app_rtc_conn.cpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 John * * 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 diff --git a/trunk/src/app/srs_app_rtc_conn.hpp b/trunk/src/app/srs_app_rtc_conn.hpp index 532cb89a7..f9ebb931c 100644 --- a/trunk/src/app/srs_app_rtc_conn.hpp +++ b/trunk/src/app/srs_app_rtc_conn.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 John * * 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 diff --git a/trunk/src/app/srs_app_sdp.cpp b/trunk/src/app/srs_app_sdp.cpp index 6c128c180..0823d35a0 100644 --- a/trunk/src/app/srs_app_sdp.cpp +++ b/trunk/src/app/srs_app_sdp.cpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 John * * 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 diff --git a/trunk/src/app/srs_app_sdp.hpp b/trunk/src/app/srs_app_sdp.hpp index 963eb3599..6297acccc 100644 --- a/trunk/src/app/srs_app_sdp.hpp +++ b/trunk/src/app/srs_app_sdp.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 John * * 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 diff --git a/trunk/src/protocol/srs_stun_stack.cpp b/trunk/src/protocol/srs_stun_stack.cpp index 4330c92f7..6dcea2950 100644 --- a/trunk/src/protocol/srs_stun_stack.cpp +++ b/trunk/src/protocol/srs_stun_stack.cpp @@ -1,3 +1,26 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 John + * + * 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 using namespace std; diff --git a/trunk/src/protocol/srs_stun_stack.hpp b/trunk/src/protocol/srs_stun_stack.hpp index db74c90b3..dda33c194 100644 --- a/trunk/src/protocol/srs_stun_stack.hpp +++ b/trunk/src/protocol/srs_stun_stack.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 John * * 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 @@ -24,9 +24,10 @@ #ifndef SRS_PROTOCOL_STUN_HPP #define SRS_PROTOCOL_STUN_HPP +#include + #include -#include #include class SrsBuffer; From 74ad7143e74f65f86a14e1cb59f19a62d6358a84 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 18:05:32 +0800 Subject: [PATCH 09/13] Change authro to Bepartofyou for RTC codec --- trunk/src/app/srs_app_audio_recode.cpp | 2 +- trunk/src/app/srs_app_audio_recode.hpp | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/trunk/src/app/srs_app_audio_recode.cpp b/trunk/src/app/srs_app_audio_recode.cpp index f6d605277..77a2a440d 100644 --- a/trunk/src/app/srs_app_audio_recode.cpp +++ b/trunk/src/app/srs_app_audio_recode.cpp @@ -2,7 +2,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 Bepartofyou * * 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 diff --git a/trunk/src/app/srs_app_audio_recode.hpp b/trunk/src/app/srs_app_audio_recode.hpp index 6dac2f07a..d37afc381 100644 --- a/trunk/src/app/srs_app_audio_recode.hpp +++ b/trunk/src/app/srs_app_audio_recode.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * Copyright (c) 2013-2020 Bepartofyou * * 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 @@ -24,9 +24,10 @@ #ifndef SRS_APP_AUDIO_RECODE_HPP #define SRS_APP_AUDIO_RECODE_HPP -#include #include +#include + #ifdef __cplusplus extern "C" { #endif From 4ac8991a8e689e914f09c2a149d3041e1b4381c9 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 18:10:44 +0800 Subject: [PATCH 10/13] Update author to runner365 for SRT --- trunk/src/srt/srt_conn.cpp | 23 +++++++++++++++++++++++ trunk/src/srt/srt_conn.hpp | 26 ++++++++++++++++++++++++++ trunk/src/srt/srt_data.cpp | 23 +++++++++++++++++++++++ trunk/src/srt/srt_data.hpp | 26 ++++++++++++++++++++++++++ trunk/src/srt/srt_handle.cpp | 22 ++++++++++++++++++++++ trunk/src/srt/srt_handle.hpp | 26 ++++++++++++++++++++++++++ trunk/src/srt/srt_server.cpp | 23 +++++++++++++++++++++++ trunk/src/srt/srt_server.hpp | 25 +++++++++++++++++++++++++ trunk/src/srt/srt_to_rtmp.cpp | 23 +++++++++++++++++++++++ trunk/src/srt/srt_to_rtmp.hpp | 26 ++++++++++++++++++++++++++ trunk/src/srt/stringex.hpp | 26 ++++++++++++++++++++++++++ trunk/src/srt/time_help.h | 26 ++++++++++++++++++++++++++ trunk/src/srt/ts_demux.cpp | 23 +++++++++++++++++++++++ trunk/src/srt/ts_demux.hpp | 26 ++++++++++++++++++++++++++ trunk/src/srt/ts_demux_test.cpp | 23 +++++++++++++++++++++++ 15 files changed, 367 insertions(+) diff --git a/trunk/src/srt/srt_conn.cpp b/trunk/src/srt/srt_conn.cpp index e2f18af27..f2564aefe 100644 --- a/trunk/src/srt/srt_conn.cpp +++ b/trunk/src/srt/srt_conn.cpp @@ -1,3 +1,26 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 "srt_conn.hpp" #include "time_help.h" #include "stringex.hpp" diff --git a/trunk/src/srt/srt_conn.hpp b/trunk/src/srt/srt_conn.hpp index adc79eaf4..beca33bcf 100644 --- a/trunk/src/srt/srt_conn.hpp +++ b/trunk/src/srt/srt_conn.hpp @@ -1,5 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 SRT_CONN_H #define SRT_CONN_H + +#include + #include "stringex.hpp" #include #include diff --git a/trunk/src/srt/srt_data.cpp b/trunk/src/srt/srt_data.cpp index 5fc766814..5e1b65d54 100644 --- a/trunk/src/srt/srt_data.cpp +++ b/trunk/src/srt/srt_data.cpp @@ -1,3 +1,26 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 "srt_data.hpp" #include diff --git a/trunk/src/srt/srt_data.hpp b/trunk/src/srt/srt_data.hpp index cc0d9604e..1d9e284dc 100644 --- a/trunk/src/srt/srt_data.hpp +++ b/trunk/src/srt/srt_data.hpp @@ -1,5 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 SRT_DATA_H #define SRT_DATA_H + +#include + #include #include diff --git a/trunk/src/srt/srt_handle.cpp b/trunk/src/srt/srt_handle.cpp index a6351548e..50f702051 100644 --- a/trunk/src/srt/srt_handle.cpp +++ b/trunk/src/srt/srt_handle.cpp @@ -1,3 +1,25 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 "srt_handle.hpp" #include "time_help.h" diff --git a/trunk/src/srt/srt_handle.hpp b/trunk/src/srt/srt_handle.hpp index d0ae14a78..54cece147 100644 --- a/trunk/src/srt/srt_handle.hpp +++ b/trunk/src/srt/srt_handle.hpp @@ -1,5 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 SRT_HANDLE_H #define SRT_HANDLE_H + +#include + #include #include diff --git a/trunk/src/srt/srt_server.cpp b/trunk/src/srt/srt_server.cpp index 7576d3bf5..76f203749 100644 --- a/trunk/src/srt/srt_server.cpp +++ b/trunk/src/srt/srt_server.cpp @@ -1,3 +1,26 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 "srt_server.hpp" #include "srt_handle.hpp" #include diff --git a/trunk/src/srt/srt_server.hpp b/trunk/src/srt/srt_server.hpp index 0ef244b4b..ecea91858 100644 --- a/trunk/src/srt/srt_server.hpp +++ b/trunk/src/srt/srt_server.hpp @@ -1,6 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 SRT_SERVER_H #define SRT_SERVER_H +#include + #include #include diff --git a/trunk/src/srt/srt_to_rtmp.cpp b/trunk/src/srt/srt_to_rtmp.cpp index c01477fb9..d4772784b 100644 --- a/trunk/src/srt/srt_to_rtmp.cpp +++ b/trunk/src/srt/srt_to_rtmp.cpp @@ -1,3 +1,26 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 "srt_to_rtmp.hpp" #include "stringex.hpp" #include "time_help.h" diff --git a/trunk/src/srt/srt_to_rtmp.hpp b/trunk/src/srt/srt_to_rtmp.hpp index 5fcaf6acf..0fe07f878 100644 --- a/trunk/src/srt/srt_to_rtmp.hpp +++ b/trunk/src/srt/srt_to_rtmp.hpp @@ -1,5 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 SRT_TO_RTMP_H #define SRT_TO_RTMP_H + +#include + #include #include #include diff --git a/trunk/src/srt/stringex.hpp b/trunk/src/srt/stringex.hpp index 36e8eb8b4..54b42b068 100644 --- a/trunk/src/srt/stringex.hpp +++ b/trunk/src/srt/stringex.hpp @@ -1,5 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 STRING_EX_H #define STRING_EX_H + +#include + #include #include #include diff --git a/trunk/src/srt/time_help.h b/trunk/src/srt/time_help.h index 301139df7..a84a03fda 100644 --- a/trunk/src/srt/time_help.h +++ b/trunk/src/srt/time_help.h @@ -1,5 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 TIME_HELP_H #define TIME_HELP_H + +#include + #include inline long long now_ms() { diff --git a/trunk/src/srt/ts_demux.cpp b/trunk/src/srt/ts_demux.cpp index db486d5ad..4666215ad 100644 --- a/trunk/src/srt/ts_demux.cpp +++ b/trunk/src/srt/ts_demux.cpp @@ -1,3 +1,26 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 "ts_demux.hpp" #include #include diff --git a/trunk/src/srt/ts_demux.hpp b/trunk/src/srt/ts_demux.hpp index 4d7c9fd5e..662f726ce 100644 --- a/trunk/src/srt/ts_demux.hpp +++ b/trunk/src/srt/ts_demux.hpp @@ -1,5 +1,31 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 TS_DEMUX_H #define TS_DEMUX_H + +#include + #include "srt_data.hpp" #include #include diff --git a/trunk/src/srt/ts_demux_test.cpp b/trunk/src/srt/ts_demux_test.cpp index e68d06218..e7af92261 100644 --- a/trunk/src/srt/ts_demux_test.cpp +++ b/trunk/src/srt/ts_demux_test.cpp @@ -1,3 +1,26 @@ +/** + * The MIT License (MIT) + * + * Copyright (c) 2013-2020 Runner365 + * + * 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 "ts_demux.hpp" #include #include From 6012ac4eb0c9355df849a420a236b1f5b6e12c6c Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 22:30:57 +0800 Subject: [PATCH 11/13] Refine code --- trunk/conf/full.conf | 128 ++++++++++++++++++++------ trunk/conf/push.gb28181.conf | 96 +++++++++---------- trunk/src/app/srs_app_config.cpp | 20 ++-- trunk/src/app/srs_app_config.hpp | 3 +- trunk/src/app/srs_app_gb28181.cpp | 4 +- trunk/src/app/srs_app_gb28181.hpp | 4 +- trunk/src/app/srs_app_gb28181_sip.cpp | 2 +- trunk/src/app/srs_app_gb28181_sip.hpp | 2 +- trunk/src/protocol/srs_sip_stack.cpp | 2 +- trunk/src/protocol/srs_sip_stack.hpp | 2 +- 10 files changed, 167 insertions(+), 96 deletions(-) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index a0b39e7ad..37f15eaa7 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 index f7a3779b6..a106016c3 100644 --- a/trunk/conf/push.gb28181.conf +++ b/trunk/conf/push.gb28181.conf @@ -3,8 +3,6 @@ listen 1935; max_connections 1000; daemon off; -pid ./objs/srs28181.pid; -srs_log_file ./objs/srs28181.log; srs_log_tank console; http_api { @@ -16,80 +14,84 @@ stream_caster { enabled on; caster gb28181; - #转发流到rtmp服务器地址与端口 + # 转发流到rtmp服务器地址与端口 + # TODO: https://github.com/ossrs/srs/pull/1679/files#r400875104 + # [stream] is VideoChannelCodecID(视频通道编码ID) output 127.0.0.1:1935; - #接收设备端rtp流的多路复用端口 + # 接收设备端rtp流的多路复用端口 listen 9000; - #rtp接收监听端口范围,最小值 + # rtp接收监听端口范围,最小值 rtp_port_min 58200; - #rtp接收监听端口范围,最大值 + # rtp接收监听端口范围,最大值 rtp_port_max 58300; - #是否等待关键帧之后,再转发, - #off:不需等待,直接转发 - #on:等第一个关键帧后,再转发 + # 是否等待关键帧之后,再转发, + # off:不需等待,直接转发 + # on:等第一个关键帧后,再转发 wait_keyframe off; - #rtp包空闲等待时间,如果指定时间没有收到任何包 - #rtp监听连接自动停止,发送BYE命令 + # 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 on; + # 是否转发音频流 + # 目前只支持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 + # 服务器主机号,可以域名或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流 + # 是否启用srs内部sip信令 + # 为on信令走srs, off 只转发ps流 enabled on; - #sip监听udp端口 - listen 15060; + # sip监听udp端口 + listen 5060; - #服务器端编号 - #设备端配置编号需要与该值一致,否则无法注册 + # SIP server ID(SIP服务器ID). + # 设备端配置编号需要与该值一致,否则无法注册 serial 34020000002000000001; - #服务器端域 + # SIP server domain(SIP服务器域) realm 3402000000; - #服务端发送ack后,接收回应的超时时间,单位为秒 - #如果指定时间没有回应,认为失败 + # 服务端发送ack后,接收回应的超时时间,单位为秒 + # 如果指定时间没有回应,认为失败 ack_timeout 30; - #设备心跳维持时间,如果指定时间内(秒)没有接收一个心跳 - #认为设备离线 - keepalive_timeout 30; + # 设备心跳维持时间,如果指定时间内(秒)没有接收一个心跳 + # 认为设备离线 + keepalive_timeout 120; - #日志打印是否打印sip信息 - #off:不打印 - #on:打印接收或发送sip命令信息 + # 日志打印是否打印sip信息 + # off:不打印 + # on:打印接收或发送sip命令信息 + # TODO: https://github.com/ossrs/srs/pull/1679/files#r400929300 print_sip_message off; - #注册之后是否自动给设备端发送invite - #on: 是 off 不是,需要通过api控制 + # 注册之后是否自动给设备端发送invite + # on: 是 off 不是,需要通过api控制 auto_play on; - #设备将流发送的端口,是否固定 - #on 发送流到多路复用端口 如9000 - #off 自动从rtp_mix_port - rtp_max_port 之间的值中 - #选一个可以用的端口 + # 设备将流发送的端口,是否固定 + # on 发送流到多路复用端口 如9000 + # off 自动从rtp_mix_port - rtp_max_port 之间的值中 + # 选一个可以用的端口 invite_port_fixed on; } } diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 6f925d068..4b5a4b895 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -4310,9 +4310,9 @@ int SrsConfig::get_stream_caster_rtp_port_max(SrsConfDirective* conf) return ::atoi(conf->arg0().c_str()); } -int SrsConfig::get_stream_caster_gb28181_rtp_idle_timeout(SrsConfDirective* conf) +srs_utime_t SrsConfig::get_stream_caster_gb28181_rtp_idle_timeout(SrsConfDirective* conf) { - static int DEFAULT = 30; + static srs_utime_t DEFAULT = 30 * SRS_UTIME_SECONDS; if (!conf) { return DEFAULT; @@ -4323,7 +4323,7 @@ int SrsConfig::get_stream_caster_gb28181_rtp_idle_timeout(SrsConfDirective* conf return DEFAULT; } - return ::atoi(conf->arg0().c_str()); + return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_SECONDS); } int SrsConfig::get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf) @@ -4349,7 +4349,7 @@ int SrsConfig::get_stream_caster_gb28181_ack_timeout(SrsConfDirective* conf) int SrsConfig::get_stream_caster_gb28181_keepalive_timeout(SrsConfDirective* conf) { - static int DEFAULT = 30; + static int DEFAULT = 120; if (!conf) { return DEFAULT; @@ -4386,7 +4386,7 @@ string SrsConfig::get_stream_caster_gb28181_host(SrsConfDirective* conf) string SrsConfig::get_stream_caster_gb28181_serial(SrsConfDirective* conf) { - static string DEFAULT = ""; + static string DEFAULT = "34020000002000000001"; if (!conf) { return DEFAULT; @@ -4407,7 +4407,7 @@ string SrsConfig::get_stream_caster_gb28181_serial(SrsConfDirective* conf) string SrsConfig::get_stream_caster_gb28181_realm(SrsConfDirective* conf) { - static string DEFAULT = ""; + static string DEFAULT = "3402000000"; if (!conf) { return DEFAULT; @@ -4428,7 +4428,7 @@ string SrsConfig::get_stream_caster_gb28181_realm(SrsConfDirective* conf) bool SrsConfig::get_stream_caster_gb28181_audio_enable(SrsConfDirective* conf) { - static bool DEFAULT = true; + static bool DEFAULT = false; if (!conf) { return DEFAULT; @@ -4481,7 +4481,7 @@ bool SrsConfig::get_stream_caster_gb28181_wait_keyframe(SrsConfDirective* conf) bool SrsConfig::get_stream_caster_gb28181_sip_enable(SrsConfDirective* conf) { - static bool DEFAULT = false; + static bool DEFAULT = true; if (!conf) { return DEFAULT; @@ -4503,7 +4503,7 @@ bool SrsConfig::get_stream_caster_gb28181_sip_enable(SrsConfDirective* conf) bool SrsConfig::get_stream_caster_gb28181_sip_auto_play(SrsConfDirective* conf) { - static bool DEFAULT = false; + static bool DEFAULT = true; if (!conf) { return DEFAULT; @@ -4525,7 +4525,7 @@ bool SrsConfig::get_stream_caster_gb28181_sip_auto_play(SrsConfDirective* conf) int SrsConfig::get_stream_caster_gb28181_sip_listen(SrsConfDirective* conf) { - static int DEFAULT = 0; + static int DEFAULT = 5060; if (!conf) { return DEFAULT; diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index 801f24ca1..cdeb861aa 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -500,7 +500,7 @@ public: // Get the max udp port for rtp of stream caster rtsp. virtual int get_stream_caster_rtp_port_max(SrsConfDirective* conf); - virtual int get_stream_caster_gb28181_rtp_idle_timeout(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); @@ -514,7 +514,6 @@ public: virtual int get_stream_caster_gb28181_sip_listen(SrsConfDirective* conf); virtual bool get_stream_caster_gb28181_sip_invite_port_fixed(SrsConfDirective* conf); - // vhost specified section public: // Get the vhost directive by vhost name. diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index 39b1714bf..29373ae16 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * 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 @@ -749,7 +749,7 @@ srs_error_t SrsGb28181RtmpMuxer::do_cycle() } srs_utime_t now = srs_get_system_time(); - srs_utime_t duration = (now - recv_stream_time) / (1000*1000); + srs_utime_t duration = now - recv_stream_time; SrsGb28181Config config = gb28181_manger->get_gb28181_config(); if (duration > config.rtp_idle_timeout){ diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 8eff26b32..812f390fb 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * 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 @@ -296,7 +296,7 @@ class SrsGb28181Config { public: std::string host; - int rtp_idle_timeout; + srs_utime_t rtp_idle_timeout; bool audio_enable; bool wait_keyframe; std::string output; diff --git a/trunk/src/app/srs_app_gb28181_sip.cpp b/trunk/src/app/srs_app_gb28181_sip.cpp index 64a8512b6..72a55685c 100644 --- a/trunk/src/app/srs_app_gb28181_sip.cpp +++ b/trunk/src/app/srs_app_gb28181_sip.cpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * 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 diff --git a/trunk/src/app/srs_app_gb28181_sip.hpp b/trunk/src/app/srs_app_gb28181_sip.hpp index 17e4313cb..9206e66c9 100644 --- a/trunk/src/app/srs_app_gb28181_sip.hpp +++ b/trunk/src/app/srs_app_gb28181_sip.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * 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 diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp index 1b5d49812..e24e743c2 100644 --- a/trunk/src/protocol/srs_sip_stack.cpp +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * 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 diff --git a/trunk/src/protocol/srs_sip_stack.hpp b/trunk/src/protocol/srs_sip_stack.hpp index 26f712081..006ecbba3 100644 --- a/trunk/src/protocol/srs_sip_stack.hpp +++ b/trunk/src/protocol/srs_sip_stack.hpp @@ -1,7 +1,7 @@ /** * The MIT License (MIT) * - * Copyright (c) 2013-2020 Winlin + * 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 From d81cde695d757b2822f1191a5c75f9f0018931e8 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 22:48:38 +0800 Subject: [PATCH 12/13] Refactor code --- trunk/src/app/srs_app_gb28181.cpp | 4 +--- trunk/src/app/srs_app_gb28181.hpp | 2 -- trunk/src/app/srs_app_http_api.cpp | 2 -- trunk/src/protocol/srs_sip_stack.cpp | 1 - 4 files changed, 1 insertion(+), 8 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index 29373ae16..ea196e61a 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -231,7 +231,7 @@ srs_error_t SrsGb28181PsRtpProcessor::on_udp_packet(const sockaddr* from, const //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; + //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 @@ -1403,8 +1403,6 @@ srs_error_t SrsGb28181Manger::start_ps_rtp_listen(std::string id, int port) 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; diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 812f390fb..b206a1ff2 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -221,8 +221,6 @@ private: std::string _rtmp_url; std::string video_ssrc; std::string audio_ssrc; - int audio_sample_rate; - int audio_channel; SrsGb28181Manger* gb28181_manger; SrsCoroutine* trd; diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 2b4e09afe..92094bffc 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -976,8 +976,6 @@ srs_error_t SrsGoApiRtcPlay::exchange_sdp(const std::string& app, const std::str local_sdp.group_policy_ = "BUNDLE"; - int mid = 0; - for (int i = 0; i < remote_sdp.media_descs_.size(); ++i) { const SrsMediaDesc& remote_media_desc = remote_sdp.media_descs_[i]; diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp index e24e743c2..c0b06e55b 100644 --- a/trunk/src/protocol/srs_sip_stack.cpp +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -99,7 +99,6 @@ 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; From 608f3d0fae4f67064419e242c0ee4c58184536c5 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 31 Mar 2020 23:06:07 +0800 Subject: [PATCH 13/13] For #1500, support push stream by GB28181. 4.0.18 --- .circleci/config.yml | 8 +++++--- README.md | 3 +++ trunk/auto/auto_headers.sh | 6 ++++++ trunk/auto/options.sh | 14 ++++++++++---- trunk/configure | 5 ++++- trunk/src/app/srs_app_http_api.cpp | 6 +++++- trunk/src/app/srs_app_http_api.hpp | 4 ++++ trunk/src/app/srs_app_server.cpp | 15 ++++++++++++++- trunk/src/app/srs_app_server.hpp | 6 ++++++ trunk/src/core/srs_core.hpp | 2 +- trunk/src/core/srs_core_version4.hpp | 2 +- 11 files changed, 59 insertions(+), 12 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 1a44c21fb..adfb1648f 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -6,15 +6,17 @@ jobs: steps: - checkout - run: | - cd trunk && ./configure --without-rtc --without-utest && make && - ./configure --with-rtc --with-utest && make clean && make + cd trunk && + ./configure --without-rtc --without-gb28181 --without-utest && make && + ./configure --with-rtc --with-gb28181 --with-utest && make clean && make test: docker: - image: ossrs/srs:dev steps: - checkout - run: | - cd trunk && ./configure --with-utest --gcov && make && + cd trunk && + ./configure --with-rtc --with-gb28181 --with-utest --gcov && make && ./objs/srs_utest && bash auto/coverage.sh workflows: version: 2 diff --git a/README.md b/README.md index 59814182d..521591734 100755 --- a/README.md +++ b/README.md @@ -136,6 +136,7 @@ For previous versions, please read: - [x] [Experimental] Support HTTP RAW API, please read [#459][bug #459], [#470][bug #470], [#319][bug #319]. - [x] [Experimental] Support SRT server, read [#1147][bug #1147]. - [x] [Experimental] Support playing stream by WebRTC, [#307][bug #307]. +- [x] [Experimental] Support push stream by GB28181, [#1500][bug #1500]. - [x] [Deprecated] Support Adobe HDS(f4m), please read wiki([CN][v2_CN_DeliveryHDS], [EN][v2_EN_DeliveryHDS]) and [#1535][bug #1535]. - [x] [Deprecated] Support bandwidth testing([CN][v1_CN_BandwidthTestTool], [EN][v1_EN_BandwidthTestTool]), please read [#1535][bug #1535]. - [x] [Deprecated] Support Adobe FMS/AMS token traverse([CN][v3_CN_DRM2], [EN][v3_EN_DRM2]) authentication, please read [#1535][bug #1535]. @@ -157,6 +158,7 @@ For previous versions, please read: ## V4 changes +* v4.0, 2020-03-31, For [#1500][bug #1500], support push stream by GB28181. 4.0.18 * v4.0, 2020-03-31, Play stream by WebRTC on iOS/Android/PC browser. 4.0.17 * v4.0, 2020-03-28, Support multiple OS/Platform build cache. 4.0.16 * v4.0, 2020-03-28, For [#1250][bug #1250], support macOS, OSX, MacbookPro, Apple Darwin. @@ -1154,6 +1156,7 @@ Maintainers of SRS project: * [Runner365](https://github.com/runner365): The focus of his work is on the [SRT](https://github.com/simple-rtmp-server/srs/wiki/v4_CN_SRTWiki) module. * [John](https://github.com/xiaozhihong): Focus on [WebRTC](https://github.com/simple-rtmp-server/srs/wiki/v4_CN_RTCWiki) module. * [B.P.Y(Bepartofyou)](https://github.com/Bepartofyou): Focus on [WebRTC](https://github.com/simple-rtmp-server/srs/wiki/v4_CN_RTCWiki) module. +* [Lixin](https://github.com/xialixin): Focus on [GB28181](https://github.com/ossrs/srs/issues/1500) module. A big THANK YOU goes to: diff --git a/trunk/auto/auto_headers.sh b/trunk/auto/auto_headers.sh index 3c4c77ea3..73249d1b2 100755 --- a/trunk/auto/auto_headers.sh +++ b/trunk/auto/auto_headers.sh @@ -79,6 +79,12 @@ else srs_undefine_macro "SRS_AUTO_RTC" $SRS_AUTO_HEADERS_H fi +if [ $SRS_GB28181 = YES ]; then + srs_define_macro "SRS_AUTO_GB28181" $SRS_AUTO_HEADERS_H +else + srs_undefine_macro "SRS_AUTO_GB28181" $SRS_AUTO_HEADERS_H +fi + if [ $SRS_MEM_WATCH = YES ]; then srs_define_macro "SRS_AUTO_MEM_WATCH" $SRS_AUTO_HEADERS_H else diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh index 81b47cdd5..c0300172b 100755 --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -18,6 +18,7 @@ help=no SRS_HDS=NO SRS_SRT=NO SRS_RTC=YES +SRS_GB28181=NO SRS_NASM=YES SRS_NGINX=NO SRS_FFMPEG_TOOL=NO @@ -136,16 +137,18 @@ Features: --with-utest Build the utest for SRS. --with-srt Build the SRT support for SRS. --with-rtc Build the WebRTC support for SRS. + --with-gb28181 Build the GB28181 support for SRS. --without-ssl Disable rtmp complex handshake. --without-hds Disable hds, the adobe http dynamic streaming. --without-stream-caster Disable stream caster, only listen and serve RTMP/HTTP. --without-stat Disable the data statistic feature. --without-librtmp Disable srs-librtmp, library for client. - --without-research Do not build the research tools. - --without-utest Do not build the utest for SRS. - --without-srt Do not build the SRT support for SRS. - --without-rtc Do not build the WebRTC support for SRS. + --without-research Disable the research tools. + --without-utest Disable the utest for SRS. + --without-srt Disable the SRT support for SRS. + --without-rtc Disable the WebRTC support for SRS. + --without-gb28181 Disable the GB28181 support for SRS. --prefix= The absolute installation path for srs. Default: $SRS_PREFIX --static Whether add '-static' to link options. @@ -231,6 +234,7 @@ function parse_user_option() { --with-utest) SRS_UTEST=YES ;; --with-srt) SRS_SRT=YES ;; --with-rtc) SRS_RTC=YES ;; + --with-gb28181) SRS_GB28181=YES ;; --with-nasm) SRS_NASM=YES ;; --with-gperf) SRS_GPERF=YES ;; --with-gmc) SRS_GPERF_MC=YES ;; @@ -249,6 +253,7 @@ function parse_user_option() { --without-utest) SRS_UTEST=NO ;; --without-srt) SRS_SRT=NO ;; --without-rtc) SRS_RTC=NO ;; + --without-gb28181) SRS_GB28181=NO ;; --without-nasm) SRS_NASM=NO ;; --without-gperf) SRS_GPERF=NO ;; --without-gmc) SRS_GPERF_MC=NO ;; @@ -550,6 +555,7 @@ function regenerate_options() { if [ $SRS_UTEST = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-utest"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-utest"; fi if [ $SRS_SRT = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-srt"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-srt"; fi if [ $SRS_RTC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-rtc"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-rtc"; fi + if [ $SRS_GB28181 = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gb28181"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gb28181"; fi if [ $SRS_NASM = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-nasm"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-nasm"; fi if [ $SRS_GPERF = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gperf"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gperf"; fi if [ $SRS_GPERF_MC = YES ]; then SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --with-gmc"; else SRS_AUTO_CONFIGURE="${SRS_AUTO_CONFIGURE} --without-gmc"; fi diff --git a/trunk/configure b/trunk/configure index 6d67ac5b5..28ab18db6 100755 --- a/trunk/configure +++ b/trunk/configure @@ -277,10 +277,13 @@ 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_gb28181" "srs_app_gb28181_sip") + "srs_app_coworkers" "srs_app_hybrid") 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 + if [[ $SRS_GB28181 == YES ]]; then + MODULE_FILES+=("srs_app_gb28181" "srs_app_gb28181_sip") + fi DEFINES="" # add each modules for app for SRS_MODULE in ${SRS_MODULES[*]}; do diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 92094bffc..35fac11cd 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1578,6 +1578,8 @@ srs_error_t SrsGoApiError::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage return srs_api_response_code(w, r, 100); } +#ifdef SRS_AUTO_GB28181 + SrsGoApiGb28181::SrsGoApiGb28181() { } @@ -1703,8 +1705,10 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa } } +#endif + SrsHttpApi::SrsHttpApi(IConnectionManager* cm, srs_netfd_t fd, SrsHttpServeMux* m, string cip) -: SrsConnection(cm, fd, cip) + : SrsConnection(cm, fd, cip) { mux = m; cors = new SrsHttpCorsMux(); diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index cf085f499..758beaba1 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -233,6 +233,8 @@ public: }; +#ifdef SRS_AUTO_GB28181 + class SrsGoApiGb28181 : public ISrsHttpHandler { public: @@ -242,6 +244,8 @@ public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; +#endif + class SrsHttpApi : virtual public SrsConnection, virtual public ISrsReloadHandler { private: diff --git a/trunk/src/app/srs_app_server.cpp b/trunk/src/app/srs_app_server.cpp index efd4ac8e1..20fbed740 100644 --- a/trunk/src/app/srs_app_server.cpp +++ b/trunk/src/app/srs_app_server.cpp @@ -349,6 +349,7 @@ SrsUdpCasterListener::~SrsUdpCasterListener() srs_freep(caster); } +#ifdef SRS_AUTO_GB28181 SrsGb28181Listener::SrsGb28181Listener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c) : SrsUdpStreamListener(svr, t, NULL) { @@ -369,6 +370,8 @@ SrsGb28181Listener::~SrsGb28181Listener() srs_freep(caster); } +#endif + SrsSignalManager* SrsSignalManager::instance = NULL; SrsSignalManager::SrsSignalManager(SrsServer* s) @@ -692,8 +695,10 @@ void SrsServer::destroy() srs_freep(signal_manager); srs_freep(conn_manager); +#ifdef SRS_AUTO_GB28181 //free global gb28281 manager srs_freep(_srs_gb28181); +#endif } void SrsServer::dispose() @@ -984,10 +989,11 @@ 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"); } - +#ifdef SRS_AUTO_GB28181 if ((err = http_api_mux->handle("/api/v1/gb28181", new SrsGoApiGb28181())) != srs_success) { return srs_error_wrap(err, "handle raw"); } +#endif // test the request info. if ((err = http_api_mux->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != srs_success) { @@ -1342,6 +1348,7 @@ srs_error_t SrsServer::listen_http_stream() return err; } +#ifdef SRS_AUTO_GB28181 srs_error_t SrsServer::listen_gb28281_sip(SrsConfDirective* stream_caster) { srs_error_t err = srs_success; @@ -1365,6 +1372,7 @@ srs_error_t SrsServer::listen_gb28281_sip(SrsConfDirective* stream_caster) return err; } +#endif srs_error_t SrsServer::listen_stream_caster() { @@ -1391,6 +1399,7 @@ srs_error_t SrsServer::listen_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)) { +#ifdef SRS_AUTO_GB28181 //init global gb28281 manger if (_srs_gb28181 == NULL){ _srs_gb28181 = new SrsGb28181Manger(stream_caster); @@ -1408,6 +1417,10 @@ srs_error_t SrsServer::listen_stream_caster() //gb28281 stream listener listener = new SrsGb28181Listener(this, SrsListenerGb28181RtpMux, stream_caster); +#else + srs_warn("gb28181 is disabled, please enable it by: ./configure --with-gb28181"); + continue; +#endif } else { return srs_error_new(ERROR_STREAM_CASTER_ENGINE, "invalid caster %s", caster.c_str()); } diff --git a/trunk/src/app/srs_app_server.hpp b/trunk/src/app/srs_app_server.hpp index dd014d210..0010929f5 100644 --- a/trunk/src/app/srs_app_server.hpp +++ b/trunk/src/app/srs_app_server.hpp @@ -164,6 +164,8 @@ public: virtual ~SrsUdpCasterListener(); }; +#ifdef SRS_AUTO_GB28181 + // A UDP gb28181 listener, for sip and rtp stream mux server. class SrsGb28181Listener : public SrsUdpStreamListener { @@ -172,6 +174,8 @@ public: virtual ~SrsGb28181Listener(); }; +#endif + // Convert signal to io, // @see: st-1.9/docs/notes.html class SrsSignalManager : public ISrsCoroutineHandler @@ -319,7 +323,9 @@ private: virtual srs_error_t listen_http_api(); virtual srs_error_t listen_http_stream(); virtual srs_error_t listen_stream_caster(); +#ifdef SRS_AUTO_GB28181 virtual srs_error_t listen_gb28281_sip(SrsConfDirective* c); +#endif // Close the listeners for specified type, // Remove the listen object from manager. virtual void close_listeners(SrsListenerType type); diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index d8c3ff8e1..7bf4c7096 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -43,7 +43,7 @@ #define RTMP_SIG_SRS_CODE "Leo" #define RTMP_SIG_SRS_URL "https://github.com/ossrs/srs" #define RTMP_SIG_SRS_LICENSE "MIT" -#define RTMP_SIG_SRS_AUTHORS "Winlin,Wenjie,Runner365,John,B.P.Y" +#define RTMP_SIG_SRS_AUTHORS "Winlin,Wenjie,Runner365,John,B.P.Y,Lixin" #define RTMP_SIG_SRS_VERSION SRS_XSTR(VERSION_MAJOR) "." SRS_XSTR(VERSION_MINOR) "." SRS_XSTR(VERSION_REVISION) #define RTMP_SIG_SRS_SERVER RTMP_SIG_SRS_KEY "/" RTMP_SIG_SRS_VERSION "(" RTMP_SIG_SRS_CODE ")" diff --git a/trunk/src/core/srs_core_version4.hpp b/trunk/src/core/srs_core_version4.hpp index 210565f1e..ddc59887f 100644 --- a/trunk/src/core/srs_core_version4.hpp +++ b/trunk/src/core/srs_core_version4.hpp @@ -24,6 +24,6 @@ #ifndef SRS_CORE_VERSION4_HPP #define SRS_CORE_VERSION4_HPP -#define SRS_VERSION4_REVISION 17 +#define SRS_VERSION4_REVISION 18 #endif