From 0d593da9989e61c51f1c4477184382e8739bb0ae Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 19:55:42 +0800 Subject: [PATCH 01/35] trunk/conf/full.conf add sip query catalog interval config --- trunk/conf/push.gb28181.conf | 6 ++-- trunk/src/app/srs_app_config.cpp | 55 ++++++++++++++++++++++++++++++-- trunk/src/app/srs_app_config.hpp | 1 + 3 files changed, 57 insertions(+), 5 deletions(-) diff --git a/trunk/conf/push.gb28181.conf b/trunk/conf/push.gb28181.conf index 0b49b8e77..27b7996ef 100644 --- a/trunk/conf/push.gb28181.conf +++ b/trunk/conf/push.gb28181.conf @@ -90,16 +90,18 @@ stream_caster { # 认为设备离线 keepalive_timeout 120; - # 注册之后是否自动给设备端发送invite # on: 是 off 不是,需要通过api控制 auto_play on; - # 设备将流发送的端口,是否固定 # on 发送流到多路复用端口 如9000 # off 自动从rtp_mix_port - rtp_max_port 之间的值中 # 选一个可以用的端口 invite_port_fixed on; + + # 向设备或下级域查询设备列表的间隔,单位(秒) + # 默认60秒 + query_catalog_interval 60; } } vhost __defaultVhost__ { diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index f63c3a07c..06c311966 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -2152,8 +2152,34 @@ srs_error_t SrsConfig::global_to_json(SrsJsonObject* obj) sobj->set(sdir->name, sdir->dumps_arg0_to_str()); } else if (sdir->name == "auto_create_channel") { sobj->set(sdir->name, sdir->dumps_arg0_to_str()); - } - } + } else if (sdir->name == "sip"){ + SrsJsonObject* ssobj = SrsJsonAny::object(); + sobj->set(sdir->name, ssobj); + + for (int j = 0; j < (int)sdir->directives.size(); j++) { + SrsConfDirective* ssdir = sdir->directives.at(j); + if (ssdir->name == "enabled") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_boolean()); + } else if (ssdir->name == "listen") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_integer()); + } else if (ssdir->name == "serial") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_str()); + } else if (ssdir->name == "realm") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_str()); + } else if (ssdir->name == "ack_timeout") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_integer()); + } else if (ssdir->name == "keepalive_timeout") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_integer()); + } else if (ssdir->name == "auto_play") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_boolean()); + } else if (ssdir->name == "invite_port_fixed") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_boolean()); + } else if (ssdir->name == "query_catalog_interval") { + ssobj->set(ssdir->name, ssdir->dumps_arg0_to_integer()); + } + } + }//end if + }//end for obj->set(dir->name, sobj); } else { continue; @@ -3686,7 +3712,8 @@ srs_error_t SrsConfig::check_normal_config() 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 != "auto_play" && m != "invite_port_fixed") { + && m != "host" && m != "serial" && m != "realm" && m != "auto_play" && m != "invite_port_fixed" + && m != "query_catalog_interval") { return srs_error_new(ERROR_SYSTEM_CONFIG_INVALID, "illegal stream_caster.%s", m.c_str()); } } @@ -4585,6 +4612,28 @@ bool SrsConfig::get_stream_caster_gb28181_auto_create_channel(SrsConfDirective* return SRS_CONF_PERFER_FALSE(conf->arg0()); } +srs_utime_t SrsConfig::get_stream_caster_gb28181_sip_query_catalog_interval(SrsConfDirective* conf) +{ + static srs_utime_t DEFAULT = 60 * SRS_UTIME_SECONDS; + + + if (!conf) { + return DEFAULT; + } + + conf = conf->get("sip"); + if (!conf) { + return DEFAULT; + } + + conf = conf->get("query_catalog_interval"); + if (!conf || conf->arg0().empty()) { + return DEFAULT; + } + + return (srs_utime_t)(::atoi(conf->arg0().c_str()) * SRS_UTIME_SECONDS); +} + int SrsConfig::get_rtc_server_enabled() { SrsConfDirective* conf = root->get("rtc_server"); diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index f7f449b08..bafa7b998 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -515,6 +515,7 @@ public: virtual int get_stream_caster_gb28181_sip_listen(SrsConfDirective* conf); virtual bool get_stream_caster_gb28181_sip_invite_port_fixed(SrsConfDirective* conf); virtual bool get_stream_caster_gb28181_auto_create_channel(SrsConfDirective* conf); + virtual srs_utime_t get_stream_caster_gb28181_sip_query_catalog_interval(SrsConfDirective* conf); // rtc section public: From 917b87a1d68e903d0d94a68ed1dc730ad274d6db Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 19:56:46 +0800 Subject: [PATCH 02/35] add sip query catalog interval config --- trunk/conf/full.conf | 3 +++ 1 file changed, 3 insertions(+) diff --git a/trunk/conf/full.conf b/trunk/conf/full.conf index 513a55b87..3c6278a24 100644 --- a/trunk/conf/full.conf +++ b/trunk/conf/full.conf @@ -370,6 +370,9 @@ stream_caster { # Whether bundle media stream port. # default: on invite_port_fixed on; + # interval to query equipment list from equipment or subordinate domain, unit(s) + # default: 60 + query_catalog_interval 60; } } From 3d1c34a45e0e62f92bb48f44f395534f7b8f66e0 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 20:00:31 +0800 Subject: [PATCH 03/35] add gbs log id define --- trunk/src/kernel/srs_kernel_error.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 5a6b8c6e5..45680a5fa 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -363,6 +363,9 @@ #define ERROR_GB28181_SIP_BYE_FAILED 6009 #define ERROR_GB28181_SIP_IS_INVITING 6010 #define ERROR_GB28181_CREATER_RTMPMUXER_FAILED 6011 +#define ERROR_GB28181_SIP_CH_OFFLINE 6012 +#define ERROR_GB28181_SIP_CH_NOTEXIST 6013 +#define ERROR_GB28181_SIP_RAW_DATA_FAILED 6014 /////////////////////////////////////////////////////// // HTTP API error. From 769efa87c388c37e2c296bf8e70b59de633f0ed0 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 20:01:35 +0800 Subject: [PATCH 04/35] add sip device channel error code --- trunk/src/kernel/srs_kernel_consts.hpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/trunk/src/kernel/srs_kernel_consts.hpp b/trunk/src/kernel/srs_kernel_consts.hpp index dc6449045..addda5696 100644 --- a/trunk/src/kernel/srs_kernel_consts.hpp +++ b/trunk/src/kernel/srs_kernel_consts.hpp @@ -178,6 +178,8 @@ #define SRS_CONSTS_LOG_EXEC "EXE" // The rtc. #define SRS_CONSTS_LOG_RTC "RTC" +// The gb28181 stream log id. +#define SRS_CONSTS_LOG_GB28181_CASTER "GBS" /////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////// From 1e0474e4707b684f50709c3ce71e7e044ac239a0 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 20:18:38 +0800 Subject: [PATCH 05/35] support sip query catalog message, parse xml to map, nvr --- trunk/src/protocol/srs_sip_stack.cpp | 419 +++++++++++++++++++++++---- trunk/src/protocol/srs_sip_stack.hpp | 11 +- 2 files changed, 380 insertions(+), 50 deletions(-) diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp index a5108fe7a..309e00473 100644 --- a/trunk/src/protocol/srs_sip_stack.cpp +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -156,6 +156,8 @@ SrsSipRequest::SrsSipRequest() sip_username = ""; peer_ip = ""; peer_port = 0; + + chid = ""; } SrsSipRequest::~SrsSipRequest() @@ -203,43 +205,48 @@ std::string SrsSipRequest::get_cmdtype_str() void SrsSipRequest::copy(SrsSipRequest* src) { - if (!src){ - return; - } - - method = src->method; - uri = src->uri; - version = src->version; - seq = src->seq; - content_type = src->content_type; - content_length = src->content_length; - call_id = src->call_id; - from = src->from; - to = src->to; - via = src->via; - from_tag = src->from_tag; - to_tag = src->to_tag; - contact = src->contact; - user_agent = src->user_agent; - branch = src->branch; - status = src->status; - expires = src->expires; - max_forwards = src->max_forwards; - www_authenticate = src->www_authenticate; - authorization = src->authorization; - cmdtype = src->cmdtype; + if (!src){ + return; + } + + method = src->method; + uri = src->uri; + version = src->version; + seq = src->seq; + content_type = src->content_type; + content_length = src->content_length; + call_id = src->call_id; + from = src->from; + to = src->to; + via = src->via; + from_tag = src->from_tag; + to_tag = src->to_tag; + contact = src->contact; + user_agent = src->user_agent; + branch = src->branch; + status = src->status; + expires = src->expires; + max_forwards = src->max_forwards; + www_authenticate = src->www_authenticate; + authorization = src->authorization; + cmdtype = src->cmdtype; - host = src->host; - host_port = src->host_port; + 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; + 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; + + chid = src->chid; + + xml_body_map = src->xml_body_map; + device_list_map = src->device_list_map; } SrsSipStack::SrsSipStack() @@ -267,6 +274,138 @@ srs_error_t SrsSipStack::parse_request(SrsSipRequest** preq, const char* recv_ms return err; } +srs_error_t SrsSipStack::parse_xml(std::string xml_msg, std::map &json_map) +{ + /* + + + Keepalive + 2034 + 34020000001110000001 + OK + + 34020000001320000002 + 34020000001320000003 + 34020000001320000005 + 34020000001320000006 + 34020000001320000007 + 34020000001320000008 + + + */ + + const char* start = xml_msg.c_str(); + const char* end = start + xml_msg.size(); + char* p = (char*)start; + + char* value_start = NULL; + + std::string xml_header; + int xml_layer = 0; + + //std::map json_map; + std::map json_key; + while (p < end) { + if (p[0] == '\n'){ + p +=1; + value_start = NULL; + } else if (p[0] == '\r' && p[1] == '\n') { + p +=2; + value_start = NULL; + } else if (p[0] == '<' && p[1] == '/') { // xml item end flag + std::string value = ""; + if (value_start) { + value = std::string(value_start, p-value_start); + } + + //skip get Notify + char *s = p; + while (p[0] != '>') {p++;} + std::string key(s, p-s); + + // get DeviceList + std::vector vec = srs_string_split(key, " "); + key = vec.at(0); + + /*xml element to map + + + 34020000001320000001 + 34020000001320000002 + + + to map is: Notify@Info@DeviceID:34020000001320000001,34020000001320000002 + */ + + //get map key + std::string mkey = ""; + for (int i = 0; i < xml_layer ; i++){ + if (mkey.empty()) { + mkey = json_key[i]; + }else{ + mkey = mkey + "@" + json_key[i]; + } + } + + //set map value + if (!mkey.empty()){ + if (json_map.find(mkey) == json_map.end()){ + json_map[mkey] = value; + }else{ + json_map[mkey] = json_map[mkey] + ","+ value; + } + } + + value_start = NULL; + xml_layer--; + + } else if (p[0] == '<') { // xml item begin flag + //skip < + p +=1; + + // get Notify + char *s = p; + while (p[0] != '>') {p++;} + std::string key(s, p-s); + + if (srs_string_contains(key, "?xml")){ + //xml header + xml_header = key; + json_map["XmlHeader"] = xml_header; + }else { + // get DeviceList + std::vector vec = srs_string_split(key, " "); + key = vec.at(0); + + //key to map by xml_layer + // + // + // + // + //json_key[0] = "Notify" + //json_key[1] = "info" + json_key[xml_layer] = key; + xml_layer++; + } + + p +=1; + value_start = p; + } else { + p++; + } + } + + // std::map::iterator it2; + // for (it2 = json_map.begin(); it2 != json_map.end(); ++it2) { + // srs_trace("========%s:%s", it2->first.c_str(), it2->second.c_str()); + // } + + return srs_success; +} + srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_msg) { srs_error_t err = srs_success; @@ -399,6 +538,47 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m } req->sip_username = req->sip_auth_id; + + //Content-Type: Application/MANSCDP+xml + if (!strcasecmp(req->content_type.c_str(),"application/manscdp+xml")){ + std::map body_map; + //xml to map + if ((err = parse_xml(body, body_map)) != srs_success) { + return srs_error_wrap(err, "sip parse xml"); + }; + + //Response Cmd + if (body_map.find("Response") != body_map.end()){ + std::string cmdtype = body_map["Response@CmdType"]; + if (cmdtype == "Catalog"){ + //Response@DeviceList@Item@DeviceID:3000001,3000002 + std::vector vec_device_id = srs_string_split(body_map["Response@DeviceList@Item@DeviceID"], ","); + //Response@DeviceList@Item@Status:ON,OFF + std::vector vec_device_status = srs_string_split(body_map["Response@DeviceList@Item@Status"], ","); + + //map key:devicd_id value:status + for(int i=0 ; idevice_list_map[vec_device_id.at(i)] = vec_device_status.at(i); + } + }else{ + //TODO: fixme + srs_trace("sip: Response cmdtype=%s not processed", cmdtype.c_str()); + } + } //Notify Cmd + else if (body_map.find("Notify") != body_map.end()){ + std::string cmdtype = body_map["Notify@CmdType"]; + if (cmdtype == "Keepalive"){ + //TODO: ???? + std::vector vec_device_id = srs_string_split(body_map["Notify@Info@DeviceID"], ","); + for(int i=0; idevice_list_map[vec_device_id.at(i)] = "OFF"; + } + }else{ + //TODO: fixme + srs_trace("sip: Notify cmdtype=%s not processed", cmdtype.c_str()); + } + }// end if(body_map) + }//end if (!strcasecmp) 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()); @@ -640,10 +820,12 @@ void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, in User-Agent: SRS/4.0.4(Leo) Content-Length: 0 */ - + char _ssrc[11]; + sprintf(_ssrc, "%010d", ssrc); + std::stringstream sdp; sdp << "v=0" << SRS_RTSP_CRLF - << "o=" << req->sip_auth_id << " 0 0 IN IP4 " << ip << SRS_RTSP_CRLF + << "o=" << req->chid << " 0 0 IN IP4 " << ip << SRS_RTSP_CRLF << "s=Play" << SRS_RTSP_CRLF << "c=IN IP4 " << ip << SRS_RTSP_CRLF << "t=0 0" << SRS_RTSP_CRLF @@ -658,20 +840,22 @@ void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, in //<< "a=rtpmap:99 H265/90000" << SRS_RTSP_CRLF //<< "a=streamMode:MAIN\r\n" //<< "a=filesize:0\r\n" - << "y=" << ssrc << SRS_RTSP_CRLF; + << "y=" << _ssrc << SRS_RTSP_CRLF; int rand = srs_sip_random(1000, 9999); - std::stringstream from, to, uri, branch, from_tag; + std::stringstream from, to, uri, branch, from_tag, call_id; //"INVITE sip:34020000001320000001@3402000000 SIP/2.0\r\n - uri << "sip:" << req->sip_auth_id << "@" << req->realm; + uri << "sip:" << req->chid << "@" << req->realm; //From: ;tag=500485%d\r\n from << req->serial << "@" << req->host << ":" << req->host_port; - to << req->sip_auth_id << "@" << req->realm; + to << req->chid << "@" << req->realm; + call_id << "2020" << rand ; req->from = from.str(); req->to = to.str(); req->uri = uri.str(); + req->call_id = call_id.str(); branch << "z9hG4bK3420" << rand; from_tag << "51235" << rand; @@ -682,13 +866,13 @@ void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, in << "Via: " << SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF << "From: " << get_sip_from(req) << SRS_RTSP_CRLF << "To: " << get_sip_to(req) << SRS_RTSP_CRLF - << "Call-ID: 20000" << rand <call_id <to << ">" << SRS_RTSP_CRLF - << "Max-Forwards: 70" << " \r\n" + << "Max-Forwards: 70" << SRS_RTSP_CRLF << "User-Agent: " << SRS_SIP_USER_AGENT <sip_auth_id << ":" << ssrc << "," << req->serial << ":0" << SRS_RTSP_CRLF + << "Subject: "<< req->chid << ":" << _ssrc << "," << req->serial << ":0" << SRS_RTSP_CRLF << "Content-Length: " << sdp.str().length() << SRS_RTSP_CRLFCRLF << sdp.str(); } @@ -703,7 +887,7 @@ void SrsSipStack::req_401_unauthorized(std::stringstream& ss, SrsSipRequest *req To: ;tag=102092689 CSeq: 1 REGISTER Call-ID: 1650345118 - User-Agent: LiveGBS v200228 + User-Agent: SRS/4.0.4(Leo) Contact: Content-Length: 0 WWW-Authenticate: Digest realm="3402000000",qop="auth",nonce="f1da98bd160f3e2efe954c6eedf5f75a" @@ -737,10 +921,10 @@ void SrsSipStack::req_ack(std::stringstream& ss, SrsSipRequest *req){ Content-Length: 0 */ - ss << "ACK " << "sip:" << req->sip_auth_id << "@" << req->realm << " "<< SRS_SIP_VERSION << SRS_RTSP_CRLF + ss << "ACK " << "sip:" << req->chid << "@" << 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" + << "To: chid << "@" << 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 @@ -775,9 +959,9 @@ void SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) */ std::stringstream from, to, uri; - uri << "sip:" << req->sip_auth_id << "@" << req->realm; + uri << "sip:" << req->chid << "@" << req->realm; from << req->serial << "@" << req->realm; - to << req->sip_auth_id << "@" << req->realm; + to << req->chid << "@" << req->realm; req->from = from.str(); req->to = to.str(); @@ -798,5 +982,142 @@ void SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) } +void SrsSipStack::req_query_catalog(std::stringstream& ss, SrsSipRequest *req) +{ + /* + //request: sip-agent <----MESSAGE Query Catalog--- sip-server + MESSAGE sip:34020000001110000001@192.168.1.21:5060 SIP/2.0 + Via: SIP/2.0/UDP 192.168.1.17:5060;rport;branch=z9hG4bK563315752 + From: ;tag=387315752 + To: + Call-ID: 728315752 + CSeq: 32 MESSAGE + Content-Type: Application/MANSCDP+xml + Max-Forwards: 70 + User-Agent: SRS/4.0.20(Leo) + Content-Length: 162 + + + + Catalog + 419315752 + 34020000001110000001 + + SIP/2.0 200 OK + Via: SIP/2.0/UDP 192.168.1.17:5060;rport=5060;branch=z9hG4bK563315752 + From: ;tag=387315752 + To: ;tag=1420696981 + Call-ID: 728315752 + CSeq: 32 MESSAGE + User-Agent: Embedded Net DVR/NVR/DVS + Content-Length: 0 + + //response: sip-agent ----MESSAGE Query Catalog---> sip-server + SIP/2.0 200 OK + Via: SIP/2.0/UDP 192.168.1.17:5060;rport=5060;received=192.168.1.17;branch=z9hG4bK563315752 + From: ;tag=387315752 + To: ;tag=1420696981 + CSeq: 32 MESSAGE + Call-ID: 728315752 + User-Agent: SRS/4.0.20(Leo) + Content-Length: 0 + + //request: sip-agent ----MESSAGE Response Catalog---> sip-server + MESSAGE sip:34020000001110000001@3402000000.spvmn.cn SIP/2.0 + Via: SIP/2.0/UDP 192.168.1.21:5060;rport;branch=z9hG4bK1681502633 + From: ;tag=1194168247 + To: + Call-ID: 685380150 + CSeq: 20 MESSAGE + Content-Type: Application/MANSCDP+xml + Max-Forwards: 70 + User-Agent: Embedded Net DVR/NVR/DVS + Content-Length: 909 + + + + Catalog + 419315752 + 34020000001110000001 + 8 + + + 34020000001320000001 + Camera 01 + Manufacturer + Camera + Owner + CivilCode +
192.168.254.18
+ 0 + 0 + 1 + 0 + ON +
+ + 34020000001320000002 + IPCamera 02 + Manufacturer + Camera + Owner + CivilCode +
192.168.254.14
+ 0 + 0 + 1 + 0 + OFF +
+
+
+ + */ + + std::stringstream xml; + std::string xmlbody; + + int sn = srs_sip_random(10000000, 99999999); + xml << "" << SRS_RTSP_CRLF + << "" << SRS_RTSP_CRLF + << "Catalog" << SRS_RTSP_CRLF + << "" << sn << "" << SRS_RTSP_CRLF + << "" << req->sip_auth_id << "" << SRS_RTSP_CRLF + << "" << SRS_RTSP_CRLF; + xmlbody = xml.str(); + + int rand = srs_sip_random(1000, 9999); + std::stringstream from, to, uri, branch, from_tag, call_id; + //"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; + call_id << "2020" << rand; + + req->from = from.str(); + req->to = to.str(); + req->uri = uri.str(); + req->call_id = call_id.str(); + + branch << "z9hG4bK3420" << rand; + from_tag << "51235" << rand; + req->branch = branch.str(); + req->from_tag = from_tag.str(); + + ss << "MESSAGE " << req->uri << " " << SRS_SIP_VERSION << SRS_RTSP_CRLF + << "Via: " << SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF + << "From: " << get_sip_from(req) << SRS_RTSP_CRLF + << "To: " << get_sip_to(req) << SRS_RTSP_CRLF + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "CSeq: 25 MESSAGE" << SRS_RTSP_CRLF + << "Content-Type: Application/MANSCDP+xml" << SRS_RTSP_CRLF + << "Max-Forwards: 70" << SRS_RTSP_CRLF + << "User-Agent: " << SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: " << xmlbody.length() << SRS_RTSP_CRLFCRLF + << xmlbody; + +} + #endif diff --git a/trunk/src/protocol/srs_sip_stack.hpp b/trunk/src/protocol/srs_sip_stack.hpp index 6e6c47d7c..1f6630429 100644 --- a/trunk/src/protocol/srs_sip_stack.hpp +++ b/trunk/src/protocol/srs_sip_stack.hpp @@ -30,6 +30,7 @@ #include #include +#include #include #include @@ -88,6 +89,11 @@ public: std::string www_authenticate; std::string authorization; + std::string chid; + + std::map xml_body_map; + std::map device_list_map; + public: std::string serial; std::string realm; @@ -131,6 +137,7 @@ 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); + virtual srs_error_t parse_xml(std::string xml_msg, std::map &json_map); private: //response from @@ -146,10 +153,12 @@ public: virtual void resp_keepalive(std::stringstream& ss, SrsSipRequest *req); //request: request sent by the sip-server, wait for sip-agent response - virtual void req_invite(std::stringstream& ss, SrsSipRequest *req, std::string ip, int port, uint32_t ssrc); + virtual void req_invite(std::stringstream& ss, SrsSipRequest *req, std::string ip, + int port, uint32_t ssrc); virtual void req_ack(std::stringstream& ss, SrsSipRequest *req); virtual void req_bye(std::stringstream& ss, SrsSipRequest *req); virtual void req_401_unauthorized(std::stringstream& ss, SrsSipRequest *req); + virtual void req_query_catalog(std::stringstream& ss, SrsSipRequest *req); }; From f74a398c1b4626c92a0dea3d6b27f5520c62cd79 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 20:19:54 +0800 Subject: [PATCH 06/35] add sip query session api --- trunk/src/app/srs_app_http_api.cpp | 31 +++++++++++++++++++++++++----- 1 file changed, 26 insertions(+), 5 deletions(-) diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 1f3749b7a..63c2af30a 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1725,7 +1725,7 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa SrsJsonArray* arr = SrsJsonAny::array(); data->set("channels", arr); - uint32_t code = _srs_gb28181->queue_stream_channel(id, arr); + uint32_t code = _srs_gb28181->query_stream_channel(id, arr); if (code != ERROR_SUCCESS) { return srs_api_response_code(w, r, code); } @@ -1733,7 +1733,8 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response(w, r, obj->dumps()); } else if(action == "sip_invite"){ - if (id.empty()){ + string chid = r->query_get("chid"); + if (id.empty() || chid.empty()){ return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); } @@ -1746,15 +1747,16 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa - int code = _srs_gb28181->notify_sip_invite(id, ip, _port, _ssrc); + int code = _srs_gb28181->notify_sip_invite(id, ip, _port, _ssrc, chid); return srs_api_response_code(w, r, code); } else if(action == "sip_bye"){ - if (id.empty()){ + string chid = r->query_get("chid"); + if (id.empty() || chid.empty()){ return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); } - int code = _srs_gb28181->notify_sip_bye(id); + int code = _srs_gb28181->notify_sip_bye(id, chid); return srs_api_response_code(w, r, code); } else if(action == "sip_raw_data"){ @@ -1775,6 +1777,25 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa int code = _srs_gb28181->notify_sip_unregister(id); return srs_api_response_code(w, r, code); } + else if(action == "sip_query_catalog"){ + if (id.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + int code = _srs_gb28181->notify_sip_query_catalog(id); + return srs_api_response_code(w, r, code); + } + else if(action == "sip_query_session"){ + SrsJsonArray* arr = SrsJsonAny::array(); + data->set("sessions", arr); + + uint32_t code = _srs_gb28181->query_sip_session(id, arr); + if (code != ERROR_SUCCESS) { + return srs_api_response_code(w, r, code); + } + + return srs_api_response(w, r, obj->dumps()); + } else { return srs_api_response_code(w, r, ERROR_GB28181_ACTION_INVALID); From 28bde1d448e7784ae4370775bf30f64efa6ed346 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 20:36:28 +0800 Subject: [PATCH 07/35] fix rtmp send timeout, first key frame wait --- trunk/src/app/srs_app_gb28181.cpp | 95 +++++++++++++++++++++---------- trunk/src/app/srs_app_gb28181.hpp | 14 +++-- 2 files changed, 73 insertions(+), 36 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index fe3fa90da..6a2fe471b 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -268,7 +268,7 @@ srs_error_t SrsGb28181PsRtpProcessor::on_udp_packet(const sockaddr* from, const } if (pprint->can_print()) { - srs_trace("<- " SRS_CONSTS_LOG_STREAM_CASTER " gb28181: client_id %s, peer(%s, %d) ps rtp packet %dB, age=%d, vt=%d/%u, sts=%u/%u/%#x, paylod=%dB", + srs_trace("<- " SRS_CONSTS_LOG_GB28181_CASTER " gb28181: client_id %s, peer(%s, %d) ps rtp packet %dB, age=%d, vt=%d/%u, sts=%u/%u/%#x, paylod=%dB", channel_id.c_str(), address_string, peer_port, nb_buf, pprint->age(), pkt.version, pkt.payload_type, pkt.sequence_number, pkt.timestamp, pkt.ssrc, pkt.payload->length() @@ -357,6 +357,7 @@ SrsPsStreamDemixer::SrsPsStreamDemixer(ISrsPsStreamHander *h, std::string id, bo audio_enable = a; wait_first_keyframe = k; channel_id = id; + first_keyframe_flag = false; } SrsPsStreamDemixer::~SrsPsStreamDemixer() @@ -404,7 +405,6 @@ int64_t SrsPsStreamDemixer::parse_ps_timestamp(const uint8_t* p) 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; @@ -589,8 +589,10 @@ srs_error_t SrsPsStreamDemixer::on_ps_stream(char* ps_data, int ps_size, uint32_ //ts=1000 seq=4 mark=true payload= audio incomplete_len = ps_size - complete_len; complete_len = complete_len + incomplete_len; + } + first_keyframe_flag = false; srs_trace("gb28181: client_id %s, unkonw ps data (%#x/%u) %02x %02x %02x %02x\n", channel_id.c_str(), ssrc, timestamp, next_ps_pack[0], next_ps_pack[1], next_ps_pack[2], next_ps_pack[3]); @@ -599,7 +601,7 @@ srs_error_t SrsPsStreamDemixer::on_ps_stream(char* ps_data, int ps_size, uint32_ } if (complete_len != ps_size){ - srs_trace("gb28181: client_id %s decode ps packet error (%#x/%u)! ps_size=%d complete=%d \n", + srs_trace("gb28181: client_id %s decode ps packet error (%#x/%u)! ps_size=%d complete=%d \n", channel_id.c_str(), ssrc, timestamp, 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) { @@ -646,6 +648,7 @@ SrsGb28181Config::SrsGb28181Config(SrsConfDirective* 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); sip_invite_port_fixed = _srs_config->get_stream_caster_gb28181_sip_invite_port_fixed(c); + sip_query_catalog_interval = _srs_config->get_stream_caster_gb28181_sip_query_catalog_interval(c); } SrsGb28181Config::~SrsGb28181Config() @@ -653,7 +656,6 @@ SrsGb28181Config::~SrsGb28181Config() } - //SrsGb28181RtmpMuxer gb28181 rtmp muxer, process ps stream to rtmp SrsGb28181RtmpMuxer::SrsGb28181RtmpMuxer(SrsGb28181Manger* c, std::string id, bool a, bool k) { @@ -675,7 +677,8 @@ SrsGb28181RtmpMuxer::SrsGb28181RtmpMuxer(SrsGb28181Manger* c, std::string id, bo wait_ps_queue = srs_cond_new(); stream_idle_timeout = -1; - recv_stream_time = 0; + recv_rtp_stream_time = 0; + send_rtmp_stream_time = 0; _rtmp_url = ""; @@ -768,7 +771,7 @@ std::string SrsGb28181RtmpMuxer::rtmp_url() srs_utime_t SrsGb28181RtmpMuxer::get_recv_stream_time() { - return recv_stream_time; + return recv_rtp_stream_time; } @@ -785,7 +788,8 @@ void SrsGb28181RtmpMuxer::destroy() srs_error_t SrsGb28181RtmpMuxer::do_cycle() { srs_error_t err = srs_success; - recv_stream_time = srs_get_system_time(); + recv_rtp_stream_time = srs_get_system_time(); + send_rtmp_stream_time = srs_get_system_time(); //consume ps stream, and check status while (true) { @@ -819,7 +823,7 @@ srs_error_t SrsGb28181RtmpMuxer::do_cycle() } srs_utime_t now = srs_get_system_time(); - srs_utime_t duration = now - recv_stream_time; + srs_utime_t duration = now - recv_rtp_stream_time; //if no RTP data is received within 2 seconds, //the peer-port and peer-ip will be cleared and @@ -831,13 +835,28 @@ srs_error_t SrsGb28181RtmpMuxer::do_cycle() channel->set_rtp_peer_port(0); channel->set_rtp_peer_ip(""); } - + 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; } + //RTMP connection is about to timeout without receiving any data., + //waiting for the next time there is data automatically connected + //it is related to the following two parameter settings of the rtmp server + //the publish 1st packet timeout in srs_utime_t + //publish_1stpkt_timeout default 20000ms + //the publish normal packet timeout in srs_utime_t + //publish_normal_timeout default 5000ms + duration = now - send_rtmp_stream_time; + bool will_timeout = duration > (5 * SRS_UTIME_SECONDS); + if (will_timeout && sdk){ + srs_warn("gb28181: client id=%s RTMP connection is about to time out without receiving any data", + channel_id.c_str()); + rtmp_close(); + } + if (ps_queue.empty()){ srs_cond_timedwait(wait_ps_queue, 200 * SRS_UTIME_MILLISECONDS); }else { @@ -862,7 +881,7 @@ void SrsGb28181RtmpMuxer::ps_packet_enqueue(SrsPsRtpPacket *pkt) { srs_assert(pkt); - recv_stream_time = srs_get_system_time(); + recv_rtp_stream_time = srs_get_system_time(); //prevent consumers from being unable to process data //and accumulating in the queue @@ -923,7 +942,7 @@ srs_error_t SrsGb28181RtmpMuxer::on_rtp_video(SrsSimpleStream *stream, int64_t f uint32_t pts = (uint32_t)(fpts / 90); srs_info("gb28181rtmpmuxer: on_rtp_video dts=%u", dts); - recv_stream_time = srs_get_system_time(); + SrsBuffer *avs = new SrsBuffer(stream->bytes(), stream->length()); SrsAutoFree(SrsBuffer, avs); @@ -1010,8 +1029,6 @@ srs_error_t SrsGb28181RtmpMuxer::on_rtp_audio(SrsSimpleStream* stream, int64_t f return srs_error_wrap(err, "jitter"); } - recv_stream_time = srs_get_system_time(); - uint32_t dts = (uint32_t)(fdts / 90); // send each frame. @@ -1167,6 +1184,8 @@ srs_error_t SrsGb28181RtmpMuxer::rtmp_write_packet(char type, uint32_t timestamp SrsSharedPtrMessage* msg = NULL; + send_rtmp_stream_time = srs_get_system_time(); + if ((err = srs_rtmp_create_msg(type, timestamp, data, size, sdk->sid(), &msg)) != srs_success) { return srs_error_wrap(err, "create message"); } @@ -1197,8 +1216,9 @@ srs_error_t SrsGb28181RtmpMuxer::connect() 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)); @@ -1368,8 +1388,8 @@ uint32_t SrsGb28181Manger::generate_ssrc(std::string id) { srand(uint(time(0))); // TODO: SSRC rules can be customized, - //gb28281 live ssrc max value 0999999999(3B9AC9FF) - //gb28281 vod ssrc max value 1999999999(773593FF) + //gb28181 live ssrc max value 0999999999(3B9AC9FF) + //gb28181 vod ssrc max value 1999999999(773593FF) uint8_t index = uint8_t(rand() % (0x0F - 0x01 + 1) + 0x01); uint32_t ssrc = 0x2FFFF00 & (hash_code(id) << 8) | index; //uint32_t ssrc = 0x00FFFFFF & (hash_code(id)); @@ -1660,7 +1680,7 @@ uint32_t SrsGb28181Manger::delete_stream_channel(std::string id) { //notify the device to stop streaming //if an internal sip service controlled channel - notify_sip_bye(id); + notify_sip_bye(id, id); SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); if (muxer){ @@ -1673,7 +1693,7 @@ uint32_t SrsGb28181Manger::delete_stream_channel(std::string id) } -uint32_t SrsGb28181Manger::queue_stream_channel(std::string id, SrsJsonArray* arr) +uint32_t SrsGb28181Manger::query_stream_channel(std::string id, SrsJsonArray* arr) { if (!id.empty()){ SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); @@ -1696,21 +1716,22 @@ uint32_t SrsGb28181Manger::queue_stream_channel(std::string id, SrsJsonArray* ar return ERROR_SUCCESS; } -uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc) +uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc, std::string chid) { if (!sip_service){ return ERROR_GB28181_SIP_NOT_RUN; } //if RTMP Muxer does not exist, you need to create - SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); + std::string key = id+"@"+chid; + SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(key); 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_channel_id(key); int code = create_stream_channel(&channel); if (code != ERROR_SUCCESS){ return code; @@ -1730,24 +1751,18 @@ uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int SrsSipRequest req; req.sip_auth_id = id; - return sip_service->send_invite(&req, ip, port, ssrc); - + return sip_service->send_invite(&req, ip, port, ssrc, chid); } -uint32_t SrsGb28181Manger::notify_sip_bye(std::string id) +uint32_t SrsGb28181Manger::notify_sip_bye(std::string id, std::string chid) { if (!sip_service){ return ERROR_GB28181_SIP_NOT_RUN; } - SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); - if (muxer){ - muxer->rtmp_close(); - } - SrsSipRequest req; req.sip_auth_id = id; - return sip_service->send_bye(&req); + return sip_service->send_bye(&req, chid); } uint32_t SrsGb28181Manger::notify_sip_raw_data(std::string id, std::string data) @@ -1772,3 +1787,23 @@ uint32_t SrsGb28181Manger::notify_sip_unregister(std::string id) sip_service->remove_session(id); return ERROR_SUCCESS; } + +uint32_t SrsGb28181Manger::notify_sip_query_catalog(std::string id) +{ + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; + } + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_query_catalog(&req); +} + +uint32_t SrsGb28181Manger::query_sip_session(std::string id, SrsJsonArray* arr) +{ + if (!sip_service){ + return ERROR_GB28181_SIP_NOT_RUN; + } + + return sip_service->query_sip_session(id, arr); +} \ No newline at end of file diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index a7cde0b85..9a4607c59 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -218,7 +218,8 @@ private: SrsPithyPrint* pprint; SrsGb28181StreamChannel *channel; int stream_idle_timeout; - srs_utime_t recv_stream_time; + srs_utime_t recv_rtp_stream_time; + srs_utime_t send_rtmp_stream_time; private: std::string channel_id; std::string _rtmp_url; @@ -313,6 +314,7 @@ public: srs_utime_t sip_keepalive_timeout; bool sip_auto_play; bool sip_invite_port_fixed; + srs_utime_t sip_query_catalog_interval; public: SrsGb28181Config(SrsConfDirective* c); @@ -392,9 +394,7 @@ private: std::map rtmpmuxers_ssrc; std::map rtmpmuxers; SrsCoroutineManager* manager; - SrsGb28181SipService* sip_service; - public: SrsGb28181Manger(SrsConfDirective* c); virtual ~SrsGb28181Manger(); @@ -415,12 +415,14 @@ public: //stream channel api uint32_t create_stream_channel(SrsGb28181StreamChannel *channel); uint32_t delete_stream_channel(std::string id); - uint32_t queue_stream_channel(std::string id, SrsJsonArray* arr); + uint32_t query_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_invite(std::string id, std::string ip, int port, uint32_t ssrc, std::string chid); + uint32_t notify_sip_bye(std::string id, std::string chid); uint32_t notify_sip_raw_data(std::string id, std::string data); uint32_t notify_sip_unregister(std::string id); + uint32_t notify_sip_query_catalog(std::string id); + uint32_t query_sip_session(std::string id, SrsJsonArray* arr); private: void destroy(); From 7826c743e454643722a9aebbbdfcdf75fa971af7 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Sat, 11 Apr 2020 20:37:07 +0800 Subject: [PATCH 08/35] support access to NVR, gb28181 system sub domain --- trunk/src/app/srs_app_gb28181_sip.cpp | 448 ++++++++++++++++++++------ trunk/src/app/srs_app_gb28181_sip.hpp | 38 ++- 2 files changed, 386 insertions(+), 100 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181_sip.cpp b/trunk/src/app/srs_app_gb28181_sip.cpp index 2c819ad17..5b9a5d5b4 100644 --- a/trunk/src/app/srs_app_gb28181_sip.cpp +++ b/trunk/src/app/srs_app_gb28181_sip.cpp @@ -65,6 +65,18 @@ std::string srs_get_sip_session_status_str(SrsGb28181SipSessionStatusType status } } +SrsGb28181Device::SrsGb28181Device() +{ + device_id = ""; + invite_status = SrsGb28181SipSessionUnkonw; + invite_time = 0; + device_status = ""; + +} + +SrsGb28181Device::~SrsGb28181Device() +{} + SrsGb28181SipSession::SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipRequest* r) { servcie = c; @@ -82,6 +94,7 @@ SrsGb28181SipSession::SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipReques _register_time = 0; _alive_time = 0; _invite_time = 0; + _query_catalog_time = 0; _peer_ip = ""; _peer_port = 0; @@ -91,6 +104,8 @@ SrsGb28181SipSession::SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipReques SrsGb28181SipSession::~SrsGb28181SipSession() { + destroy(); + srs_freep(req); srs_freep(trd); srs_freep(pprint); @@ -107,12 +122,25 @@ srs_error_t SrsGb28181SipSession::serve() return err; } +void SrsGb28181SipSession::destroy() +{ + //destory all device + std::map::iterator it; + for (it = _device_list.begin(); it != _device_list.end(); ++it) { + srs_freep(it->second); + } + + _device_list.clear(); +} + srs_error_t SrsGb28181SipSession::do_cycle() { srs_error_t err = srs_success; _register_time = srs_get_system_time(); _alive_time = srs_get_system_time(); _invite_time = srs_get_system_time(); + //call it immediately after alive ok; + _query_catalog_time = 0; while (true) { @@ -121,14 +149,77 @@ srs_error_t SrsGb28181SipSession::do_cycle() if ((err = trd->pull()) != srs_success) { return srs_error_wrap(err, "gb28181 sip session cycle"); } - + + SrsGb28181Config *config = servcie->get_config(); srs_utime_t now = srs_get_system_time(); srs_utime_t reg_duration = now - _register_time; srs_utime_t alive_duration = now - _alive_time; - srs_utime_t invite_duration = now - _invite_time; - SrsGb28181Config *config = servcie->get_config(); + srs_utime_t query_duration = now - _query_catalog_time; + + //send invite, play client av + //start ps rtp listen, recv ps stream + if (_register_status == SrsGb28181SipSessionRegisterOk && + _alive_status == SrsGb28181SipSessionAliveOk) + { + std::map::iterator it; + for (it = _device_list.begin(); it != _device_list.end(); it++) { + SrsGb28181Device *device = it->second; + std::string chid = it->first; + + //update device invite time + srs_utime_t invite_duration = 0; + if (device->invite_time != 0){ + invite_duration = srs_get_system_time() - device->invite_time; + } + + //It is possible that the camera head keeps pushing and opening, + //and the duration will be very large. It will take 1 day to update + if (invite_duration > 24 * SRS_UTIME_HOURS){ + device->invite_time = srs_get_system_time(); + } + + if (device->invite_status == SrsGb28181SipSessionTrying && + invite_duration > config->sip_ack_timeout){ + device->invite_status = SrsGb28181SipSessionUnkonw; + } + + if (!config->sip_auto_play) continue; + + //offline or already invite device does not need to send invite + if (device->device_status != "ON" || + device->invite_status != SrsGb28181SipSessionUnkonw) continue; + + SrsGb28181StreamChannel ch; + + ch.set_channel_id(_session_id + "@" + chid); + ch.set_ip(config->host); + + if (config->sip_invite_port_fixed){ + ch.set_port_mode(RTP_PORT_MODE_FIXED); + }else { + ch.set_port_mode(RTP_PORT_MODE_RANDOM); + } + + //create stream channel, ready for recv device av stream + int code = _srs_gb28181->create_stream_channel(&ch); + + if (code == ERROR_SUCCESS){ + SrsSipRequest req; + req.sip_auth_id = _session_id; + + //send invite to device, req push av stream + code = servcie->send_invite(&req, ch.get_ip(), + ch.get_rtp_port(), ch.get_ssrc(), chid); + + //the same device can't be sent too fast. the device can't handle it + srs_usleep(1*SRS_UTIME_SECONDS); + } + + srs_trace("gb28181: %s clients device=%s send invite code=%d", + _session_id.c_str(), chid.c_str(), code); + }//end for (it) + }//end if (config) - if (_register_status == SrsGb28181SipSessionRegisterOk && reg_duration > _reg_expires){ srs_trace("gb28181: sip session=%s register expire", _session_id.c_str()); @@ -142,29 +233,43 @@ srs_error_t SrsGb28181SipSession::do_cycle() break; } - if (_invite_status == SrsGb28181SipSessionTrying && - invite_duration > config->sip_ack_timeout){ - _invite_status == SrsGb28181SipSessionUnkonw; - } + //query device channel + if (_alive_status == SrsGb28181SipSessionAliveOk && + query_duration >= config->sip_query_catalog_interval) { + SrsSipRequest req; + req.sip_auth_id = _session_id; + servcie->send_query_catalog(&req); + _query_catalog_time = srs_get_system_time(); - if (pprint->can_print()){ - srs_trace("gb28181: sip session=%s peer(%s, %d) status(%s,%s,%s) duration(%u,%u,%u)", + //print device status + srs_trace("gb28181: sip session=%s peer(%s, %d) status(%s,%s) duration(%u,%u)", _session_id.c_str(), _peer_ip.c_str(), _peer_port, srs_get_sip_session_status_str(_register_status).c_str(), srs_get_sip_session_status_str(_alive_status).c_str(), - srs_get_sip_session_status_str(_invite_status).c_str(), (reg_duration / SRS_UTIME_SECONDS), - (alive_duration / SRS_UTIME_SECONDS), - (invite_duration / SRS_UTIME_SECONDS)); + (alive_duration / SRS_UTIME_SECONDS)); + + std::map::iterator it; + for (it = _device_list.begin(); it != _device_list.end(); it++) { + SrsGb28181Device *device = it->second; + std::string chid = it->first; - //It is possible that the camera head keeps pushing and opening, - //and the duration will be very large. It will take 1 day to update - if (invite_duration > 24 * SRS_UTIME_HOURS){ - _invite_time = srs_get_system_time(); + srs_utime_t invite_duration = srs_get_system_time() - device->invite_time; + + if (device->invite_status != SrsGb28181SipSessionTrying && + device->invite_status != SrsGb28181SipSessionInviteOk){ + invite_duration = 0; + } + + srs_trace("gb28181: sip session=%s device=%s status(%s, %s), duration(%u)", + _session_id.c_str(), chid.c_str(), device->device_status.c_str(), + srs_get_sip_session_status_str(device->invite_status).c_str(), + (invite_duration / SRS_UTIME_SECONDS)); } } - srs_usleep(5* SRS_UTIME_SECONDS); - } + + srs_usleep(1 * SRS_UTIME_SECONDS); + }//end while return err; } @@ -191,6 +296,72 @@ srs_error_t SrsGb28181SipSession::cycle() return err; } +void SrsGb28181SipSession::update_device_list(std::map lst) +{ + std::map::iterator it; + for (it = lst.begin(); it != lst.end(); ++it) { + std::string id = it->first; + std::string status = it->second; + + if (_device_list.find(id) == _device_list.end()){ + SrsGb28181Device *device = new SrsGb28181Device(); + device->device_id = id; + device->device_status = status; + device->invite_status = SrsGb28181SipSessionUnkonw; + device->invite_time = 0; + _device_list[id] = device; + + }else { + SrsGb28181Device *device = _device_list[id]; + device->device_status = status; + } + + // srs_trace("gb28181: sip session %s, deviceid=%s status=(%s,%s)", + // _session_id.c_str(), id.c_str(), status.c_str(), + // srs_get_sip_session_status_str(device.invite_status).c_str()); + } +} + +SrsGb28181Device* SrsGb28181SipSession::get_device_info(std::string chid) +{ + if (_device_list.find(chid) != _device_list.end()){ + return _device_list[chid]; + } + return NULL; +} + +void SrsGb28181SipSession::dumps(SrsJsonObject* obj) +{ + obj->set("id", SrsJsonAny::str(_session_id.c_str())); + obj->set("device_sumnum", SrsJsonAny::integer(_device_list.size())); + + SrsJsonArray* arr = SrsJsonAny::array(); + obj->set("devices", arr); + std::map::iterator it; + for (it = _device_list.begin(); it != _device_list.end(); ++it) { + SrsGb28181Device *device = it->second; + SrsJsonObject* obj = SrsJsonAny::object(); + arr->append(obj); + obj->set("device_id", SrsJsonAny::str(device->device_id.c_str())); + obj->set("device_status", SrsJsonAny::str(device->device_status.c_str())); + obj->set("invite_status", SrsJsonAny::str(srs_get_sip_session_status_str(device->invite_status).c_str())); + obj->set("invite_time", SrsJsonAny::integer(device->invite_time/SRS_UTIME_SECONDS)); + } + + //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("rtmp_url", SrsJsonAny::str(rtmp_url.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())); + // obj->set("recv_time", SrsJsonAny::integer(recv_time/SRS_UTIME_SECONDS)); + // obj->set("recv_time_str", SrsJsonAny::str(recv_time_str.c_str())); +} + //gb28181 sip Service SrsGb28181SipService::SrsGb28181SipService(SrsConfDirective* c) { @@ -232,8 +403,9 @@ srs_error_t SrsGb28181SipService::on_udp_packet(const sockaddr* from, const int } 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); + + std::string recv_msg(buf, nb_buf); + srs_error_t err = on_udp_sip(peer_ip, peer_port, recv_msg, (sockaddr*)from, fromlen); if (err != srs_success) { return srs_error_wrap(err, "process udp"); } @@ -241,20 +413,23 @@ srs_error_t SrsGb28181SipService::on_udp_packet(const sockaddr* from, const int } srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port, - char* buf, int nb_buf, sockaddr* from, const int fromlen) + std::string recv_msg, sockaddr* from, const int fromlen) { srs_error_t err = srs_success; - srs_info("gb28181: request peer(%s, %d) nbbuf=%d", peer_ip.c_str(), peer_port, nb_buf); - srs_info("gb28181: request recv message=%s", buf); + int recv_len = recv_msg.size(); + char* recv_data = (char*)recv_msg.c_str(); + + srs_info("gb28181: request peer(%s, %d) nbbuf=%d", peer_ip.c_str(), peer_port, recv_len); + srs_info("gb28181: request recv message=%s", recv_data); - if (nb_buf < 10) { + if (recv_len < 10) { return err; } SrsSipRequest* req = NULL; - if ((err = sip->parse_request(&req, buf, nb_buf)) != srs_success) { + if ((err = sip->parse_request(&req, recv_data, recv_len)) != srs_success) { return srs_error_wrap(err, "parse sip request"); } @@ -273,7 +448,7 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port, } srs_trace("gb28181: request client id=%s peer(%s, %d)", req->sip_auth_id.c_str(), peer_ip.c_str(), peer_port); - srs_trace("gb28181: request %s method=%s, uri=%s, version=%s expires=%d", + srs_trace("gb28181: %s method=%s, uri=%s, version=%s expires=%d", req->get_cmdtype_str().c_str(), req->method.c_str(), req->uri.c_str(), req->version.c_str(), req->expires); @@ -301,62 +476,33 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port, //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((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) - { - srs_trace("gb28181: request client id=%s, peer(%s, %d)", req->sip_auth_id.c_str(), - peer_ip.c_str(), peer_port); - 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()); - - SrsGb28181StreamChannel ch; - ch.set_channel_id(session_id); - ch.set_ip(config->host); - 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()); - } - + + //update device list + if (req->device_list_map.size() > 0){ + sip_session->update_device_list(req->device_list_map); } + }else if (req->is_invite()) { - SrsGb28181SipSession* sip_session = fetch(session_id); + SrsGb28181SipSession* sip_session = fetch_session_by_callid(req->call_id); srs_trace("gb28181: request client id=%s, peer(%s, %d)", req->sip_auth_id.c_str(), peer_ip.c_str(), peer_port); - srs_trace("gb28181: request %s method=%s, uri=%s, version=%s ", + 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()); if (!sip_session){ - srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); + srs_trace("gb28181: call_id %s not map %s client ", req->call_id.c_str(), req->sip_auth_id.c_str()); return err; } - sip_session->set_sockaddr((sockaddr)*from); - sip_session->set_sockaddr_len(fromlen); + // sip_session->set_sockaddr((sockaddr)*from); + // sip_session->set_sockaddr_len(fromlen); if (sip_session->register_status() == SrsGb28181SipSessionUnkonw || sip_session->alive_status() == SrsGb28181SipSessionUnkonw) { @@ -364,37 +510,59 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port, return err; } - if (req->cmdtype == SrsSipCmdRespone && req->status == "200") { + if (req->cmdtype == SrsSipCmdRespone){ 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(srs_get_system_time()); + + if (req->status == "200") { + send_ack(req, from, fromlen); + SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id); + if (device){ + device->invite_status = SrsGb28181SipSessionInviteOk; + device->req_inivate.copy(req); + device->invite_time = srs_get_system_time(); + } + }else{ + send_ack(req, from, fromlen); + SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id); + if (device){ + device->req_inivate.copy(req); + device->invite_status = SrsGb28181SipSessionUnkonw; + device->invite_time = srs_get_system_time(); + } + } } + }else if (req->is_bye()) { srs_trace("gb28181: request client id=%s, peer(%s, %d)", req->sip_auth_id.c_str(), peer_ip.c_str(), peer_port); - srs_trace("gb28181: request %s method=%s, uri=%s, version=%s ", + 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()); - SrsGb28181SipSession* sip_session = fetch(session_id); send_status(req, from, fromlen); + SrsGb28181SipSession* sip_session = fetch_session_by_callid(req->call_id); + srs_trace("gb28181: request client id=%s, peer(%s, %d)", req->sip_auth_id.c_str(), + peer_ip.c_str(), peer_port); + 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()); + if (!sip_session){ - srs_trace("gb28181: %s client not registered", req->sip_auth_id.c_str()); + srs_trace("gb28181: call_id %s not map %s client ", req->call_id.c_str(), req->sip_auth_id.c_str()); return err; } - sip_session->set_sockaddr((sockaddr)*from); - sip_session->set_sockaddr_len(fromlen); - - sip_session->set_invite_status(SrsGb28181SipSessionBye); - sip_session->set_invite_time(srs_get_system_time()); - + if (req->cmdtype == SrsSipCmdRespone){ + srs_trace("gb28181: BYE %s client status=%s", req->sip_auth_id.c_str(), req->status.c_str()); + + if (req->status == "200") { + srs_trace("gb28181: BYE response %s client status=%s", req->sip_auth_id.c_str(), req->status.c_str()); + SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id); + if (device){ + device->invite_status = SrsGb28181SipSessionBye; + device->invite_time = srs_get_system_time(); + } + } + } }else{ srs_trace("gb28181: ingor request method=%s", req->method.c_str()); } @@ -427,6 +595,7 @@ int SrsGb28181SipService::send_ack(SrsSipRequest *req, sockaddr *f, int l) req->host_port = config->sip_port; req->realm = config->sip_realm; req->serial = config->sip_serial; + req->chid = req->sip_auth_id; sip->req_ack(ss, req); return send_message(f, l, ss); @@ -448,7 +617,7 @@ int SrsGb28181SipService::send_status(SrsSipRequest *req, sockaddr *f, int l) } -int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, uint32_t ssrc) +int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, uint32_t ssrc, std::string chid) { srs_assert(req); @@ -460,19 +629,25 @@ int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, //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){ + SrsGb28181Device *device = sip_session->get_device_info(chid); + if (!device || device->device_status != "ON"){ + return ERROR_GB28181_SIP_CH_OFFLINE; + } + + if (device->invite_status == SrsGb28181SipSessionTrying || + device->invite_status == SrsGb28181SipSessionInviteOk){ return ERROR_GB28181_SIP_IS_INVITING; } - + req->host = config->host; req->host_port = config->sip_port; req->realm = config->sip_realm; req->serial = config->sip_serial; + req->chid = chid; std::stringstream ss; sip->req_invite(ss, req, ip, port, ssrc); - + sockaddr addr = sip_session->sockaddr_from(); if (send_message(&addr, sip_session->sockaddr_fromlen(), ss) <= 0) @@ -480,13 +655,19 @@ int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, return ERROR_GB28181_SIP_INVITE_FAILED; } - sip_session->set_invite_status(SrsGb28181SipSessionTrying); + //prame branch, from_tag, to_tag, call_id, + //The parameter of 'bye' must be the same as 'invite' + device->req_inivate.copy(req); + device->invite_time = srs_get_system_time(); + device->invite_status = SrsGb28181SipSessionTrying; + + //call_id map sip_session + sip_session_map_by_callid(sip_session, req->call_id); return ERROR_SUCCESS; - } -int SrsGb28181SipService::send_bye(SrsSipRequest *req) +int SrsGb28181SipService::send_bye(SrsSipRequest *req, std::string chid) { srs_assert(req); @@ -496,15 +677,26 @@ int SrsGb28181SipService::send_bye(SrsSipRequest *req) return ERROR_GB28181_SESSION_IS_NOTEXIST; } + SrsGb28181Device *device = sip_session->get_device_info(chid); + if (!device){ + return ERROR_GB28181_SIP_CH_NOTEXIST; + } + // if (status == SrsGb28181SipSessionTrying || + // status == SrsGb28181SipSessionInviteOk){ + // return ERROR_GB28181_SIP_IS_INVITING; + // } + //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); + //SrsSipRequest r = sip_session->request(); - req->host = config->host; + req->copy(&device->req_inivate); + + req->host = config->host; req->host_port = config->sip_port; req->realm = config->sip_realm; req->serial = config->sip_serial; + req->chid = chid; //get protocol stack std::stringstream ss; @@ -535,7 +727,45 @@ int SrsGb28181SipService::send_sip_raw_data(SrsSipRequest *req, std::string dat sockaddr addr = sip_session->sockaddr_from(); if (send_message(&addr, sip_session->sockaddr_fromlen(), ss) <= 0) { - return ERROR_GB28181_SIP_BYE_FAILED; + return ERROR_GB28181_SIP_RAW_DATA_FAILED; + } + + return ERROR_SUCCESS; +} + +int SrsGb28181SipService::send_query_catalog(SrsSipRequest *req) +{ + req->host = config->host; + req->host_port = config->sip_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + req->chid = req->sip_auth_id; + + //get protocol stack + std::stringstream ss; + sip->req_query_catalog(ss, req); + + return send_sip_raw_data(req, ss.str()); +} + +int SrsGb28181SipService::query_sip_session(std::string sid, SrsJsonArray* arr) +{ + if (!sid.empty()){ + SrsGb28181SipSession* sess = fetch(sid); + if (!sess){ + return ERROR_GB28181_SESSION_IS_NOTEXIST; + } + SrsJsonObject* obj = SrsJsonAny::object(); + arr->append(obj); + sess->dumps(obj); + }else { + std::map::iterator it; + for (it = sessions.begin(); it != sessions.end(); ++it) { + SrsGb28181SipSession* sess = it->second; + SrsJsonObject* obj = SrsJsonAny::object(); + arr->append(obj); + sess->dumps(obj); + } } return ERROR_SUCCESS; @@ -596,4 +826,30 @@ void SrsGb28181SipService::destroy() sessions.clear(); } +void SrsGb28181SipService::sip_session_map_by_callid(SrsGb28181SipSession *sess, std::string call_id) +{ + if (sessions_by_callid.find(call_id) == sessions_by_callid.end()) { + sessions_by_callid[call_id] = sess; + } +} + +void SrsGb28181SipService::sip_session_unmap_by_callid(std::string call_id) +{ + std::map::iterator it = sessions_by_callid.find(call_id); + if (it != sessions_by_callid.end()) { + sessions_by_callid.erase(it); + } +} + +SrsGb28181SipSession* SrsGb28181SipService::fetch_session_by_callid(std::string call_id) +{ + SrsGb28181SipSession* session = NULL; + if (sessions_by_callid.find(call_id) == sessions_by_callid.end()) { + return NULL; + } + + session = sessions_by_callid[call_id]; + return session; +} + diff --git a/trunk/src/app/srs_app_gb28181_sip.hpp b/trunk/src/app/srs_app_gb28181_sip.hpp index d42d59e41..df30b254f 100644 --- a/trunk/src/app/srs_app_gb28181_sip.hpp +++ b/trunk/src/app/srs_app_gb28181_sip.hpp @@ -41,6 +41,7 @@ class SrsSipRequest; class SrsGb28181Config; class SrsSipStack; class SrsGb28181SipService; +class SrsGb28181Device; enum SrsGb28181SipSessionStatusType{ SrsGb28181SipSessionUnkonw = 0, @@ -51,6 +52,19 @@ enum SrsGb28181SipSessionStatusType{ SrsGb28181SipSessionBye = 5, }; +class SrsGb28181Device +{ +public: + SrsGb28181Device(); + virtual ~SrsGb28181Device(); +public: + std::string device_id; + std::string device_status; + SrsGb28181SipSessionStatusType invite_status; + srs_utime_t invite_time; + SrsSipRequest req_inivate; +}; + class SrsGb28181SipSession: public ISrsCoroutineHandler, public ISrsConnection { private: @@ -67,6 +81,7 @@ private: srs_utime_t _alive_time; srs_utime_t _invite_time; srs_utime_t _reg_expires; + srs_utime_t _query_catalog_time; std::string _peer_ip; int _peer_port; @@ -75,10 +90,16 @@ private: int _fromlen; SrsSipRequest *req; + std::map _device_list; + //std::map _device_status; + public: SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipRequest* r); virtual ~SrsGb28181SipSession(); +private: + void destroy(); + public: void set_register_status(SrsGb28181SipSessionStatusType s) { _register_status = s;} void set_alive_status(SrsGb28181SipSessionStatusType s) { _alive_status = s;} @@ -94,7 +115,6 @@ public: void set_sockaddr_len(int l) { _fromlen = l;} void set_request(SrsSipRequest *r) { req->copy(r);} - SrsGb28181SipSessionStatusType register_status() { return _register_status;} SrsGb28181SipSessionStatusType alive_status() { return _alive_status;} SrsGb28181SipSessionStatusType invite_status() { return _invite_status;} @@ -110,6 +130,10 @@ public: SrsSipRequest request() { return *req;} std::string session_id() { return _session_id;} +public: + void update_device_list(std::map devlist); + SrsGb28181Device *get_device_info(std::string chid); + void dumps(SrsJsonObject* obj); public: virtual srs_error_t serve(); @@ -130,6 +154,7 @@ private: srs_netfd_t lfd; std::map sessions; + std::map sessions_by_callid; public: SrsGb28181SipService(SrsConfDirective* c); virtual ~SrsGb28181SipService(); @@ -140,15 +165,16 @@ public: 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); + srs_error_t on_udp_sip(std::string host, int port, std::string recv_msg, 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); + int send_invite(SrsSipRequest *req, std::string ip, int port, uint32_t ssrc, std::string chid); + int send_bye(SrsSipRequest *req, std::string chid); + int send_query_catalog(SrsSipRequest *req); // The SIP command is transmitted through HTTP API, // and the body content is transmitted to the device, @@ -165,6 +191,7 @@ public: // // int send_sip_raw_data(SrsSipRequest *req, std::string data); + int query_sip_session(std::string sid, SrsJsonArray* arr); public: srs_error_t fetch_or_create_sip_session(SrsSipRequest *req, SrsGb28181SipSession** sess); @@ -172,6 +199,9 @@ public: void remove_session(std::string id); SrsGb28181Config* get_config(); + void sip_session_map_by_callid(SrsGb28181SipSession *sess, std::string call_id); + void sip_session_unmap_by_callid(std::string call_id); + SrsGb28181SipSession* fetch_session_by_callid(std::string call_id); }; #endif From 95b869245b92f72a841e91a3ce4ce3acefa4311c Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Wed, 15 Apr 2020 12:06:27 +0800 Subject: [PATCH 09/35] fix sip message parse error --- trunk/src/app/srs_app_gb28181_sip.cpp | 4 +++ trunk/src/kernel/srs_kernel_error.hpp | 1 + trunk/src/protocol/srs_sip_stack.cpp | 51 ++++++++++++++++++++------- 3 files changed, 43 insertions(+), 13 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181_sip.cpp b/trunk/src/app/srs_app_gb28181_sip.cpp index 5b9a5d5b4..9348a35e2 100644 --- a/trunk/src/app/srs_app_gb28181_sip.cpp +++ b/trunk/src/app/srs_app_gb28181_sip.cpp @@ -441,6 +441,10 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port, if (req->is_register()) { std::vector serial = srs_string_split(srs_string_replace(req->uri,"sip:", ""), "@"); + if (serial.empty()){ + return srs_error_new(ERROR_GB28181_SIP_PRASE_FAILED, "register string split"); + } + if (serial.at(0) != config->sip_serial){ srs_warn("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()); diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 45680a5fa..9b0e0578e 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -366,6 +366,7 @@ #define ERROR_GB28181_SIP_CH_OFFLINE 6012 #define ERROR_GB28181_SIP_CH_NOTEXIST 6013 #define ERROR_GB28181_SIP_RAW_DATA_FAILED 6014 +#define ERROR_GB28181_SIP_PRASE_FAILED 6015 /////////////////////////////////////////////////////// // HTTP API error. diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp index 309e00473..e29fc4876 100644 --- a/trunk/src/protocol/srs_sip_stack.cpp +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -328,6 +328,10 @@ srs_error_t SrsSipStack::parse_xml(std::string xml_msg, std::map get DeviceList std::vector vec = srs_string_split(key, " "); + if (vec.empty()){ + return srs_error_new(ERROR_GB28181_SIP_PRASE_FAILED, "prase xml"); + } + key = vec.at(0); /*xml element to map @@ -378,6 +382,10 @@ srs_error_t SrsSipStack::parse_xml(std::string xml_msg, std::map get DeviceList std::vector vec = srs_string_split(key, " "); + if (vec.empty()){ + return srs_error_new(ERROR_GB28181_SIP_PRASE_FAILED, "prase xml"); + } + key = vec.at(0); //key to map by xml_layer @@ -411,7 +419,13 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m srs_error_t err = srs_success; std::vector header_body = srs_string_split(recv_msg, SRS_RTSP_CRLFCRLF); + if (header_body.empty()){ + return srs_error_new(ERROR_GB28181_SIP_PRASE_FAILED, "parse reques message"); + } + std::string header = header_body.at(0); + //Must be added SRS_RTSP_CRLFCRLF in order to handle the last line header + header += SRS_RTSP_CRLFCRLF; std::string body = ""; if (header_body.size() > 1){ @@ -435,7 +449,7 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m newline_start = p; if (firstline == ""){ - firstline = oneline; + firstline = srs_string_replace(oneline, "\r\n", ""); srs_info("sip: first line=%s", firstline.c_str()); }else{ size_t pos = oneline.find(":"); @@ -450,7 +464,7 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m if (!strcasecmp(phead, "call-id:")) { std::vector vec_callid = srs_string_split(content, " "); - req->call_id = vec_callid.at(0); + req->call_id = vec_callid.empty() ? "" : vec_callid.at(0); } else if (!strcasecmp(phead, "contact:")) { req->contact = content; @@ -466,8 +480,9 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m } 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); + std::string seq = vec_seq.empty() ? "" : vec_seq.at(0); + req->seq = strtoul(seq.c_str(), NULL, 10); + req->method = vec_seq.size() > 0 ? vec_seq.at(1) : ""; } else if (!strcasecmp(phead, "from:")) { content = srs_string_replace(content, "sip:", ""); @@ -484,7 +499,6 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m } } else if (!strcasecmp(phead, "via:")) { - //std::vector vec_via = srs_string_split(content, ";"); req->via = content; req->branch = srs_sip_get_param(content.c_str(), "branch"); } @@ -516,25 +530,32 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m } std::vector method_uri_ver = srs_string_split(firstline, " "); + + if (method_uri_ver.empty()) { + return srs_error_new(ERROR_GB28181_SIP_PRASE_FAILED, "parse request firstline is empty"); + } + //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->status = method_uri_ver.size() > 0 ? 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:", ""); + std::string ss = str.empty() ? "" : str.at(0); + req->sip_auth_id = srs_string_replace(ss, "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); + req->uri = method_uri_ver.size() > 0 ? method_uri_ver.at(1) : ""; + req->version = method_uri_ver.size() > 1 ? method_uri_ver.at(2) : ""; vector str = srs_string_split(req->from, "@"); - req->sip_auth_id = srs_string_replace(str.at(0), "sip:", ""); + std::string ss = str.empty() ? "" : str.at(0); + req->sip_auth_id = srs_string_replace(ss, "sip:", ""); } req->sip_username = req->sip_auth_id; @@ -625,8 +646,12 @@ std::string SrsSipStack::get_sip_via(SrsSipRequest const *req) { std::string via = srs_string_replace(req->via, SRS_SIP_VERSION"/UDP ", ""); std::vector vec_via = srs_string_split(via, ";"); - std::string ip_port = vec_via.at(0); + + std::string ip_port = vec_via.empty() ? "" : vec_via.at(0); std::vector vec_ip_port = srs_string_split(ip_port, ":"); + + std::string ip = vec_ip_port.empty() ? "" : vec_ip_port.at(0); + std::string port = vec_ip_port.size() > 0 ? vec_ip_port.at(1) : ""; std::string branch, rport, received; if (req->branch.empty()){ @@ -635,8 +660,8 @@ std::string SrsSipStack::get_sip_via(SrsSipRequest const *req) branch = ";branch=" + req->branch; } - received = ";received=" + vec_ip_port.at(0); - rport = ";rport=" + vec_ip_port.at(1); + received = ";received=" + ip; + rport = ";rport=" + port; return SRS_SIP_VERSION"/UDP " + ip_port + rport + received + branch; } From 7950bc586edc01e6b7c65cf9d4c0a57405f929fb Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Wed, 15 Apr 2020 22:05:32 +0800 Subject: [PATCH 10/35] add gb28281 demo html --- trunk/research/players/rtc_player.html | 1 + trunk/research/players/srs_bwt.html | 1 + trunk/research/players/srs_chat.html | 1 + trunk/research/players/srs_gb28181.html | 1172 ++++++++++++++++++++ trunk/research/players/srs_player.html | 1 + trunk/research/players/srs_publisher.html | 1 + trunk/research/players/srs_publisher2.html | 1 + trunk/research/players/vlc.html | 1 + 8 files changed, 1179 insertions(+) create mode 100644 trunk/research/players/srs_gb28181.html diff --git a/trunk/research/players/rtc_player.html b/trunk/research/players/rtc_player.html index e1eb61eeb..719d710ea 100644 --- a/trunk/research/players/rtc_player.html +++ b/trunk/research/players/rtc_player.html @@ -30,6 +30,7 @@
  • VLC播放器
  • +
  • SRS-GB28181
  • diff --git a/trunk/research/players/srs_bwt.html b/trunk/research/players/srs_bwt.html index 53d893c7e..408516d9c 100644 --- a/trunk/research/players/srs_bwt.html +++ b/trunk/research/players/srs_bwt.html @@ -30,6 +30,7 @@
  • VLC播放器
  • +
  • SRS-GB28181
  • diff --git a/trunk/research/players/srs_chat.html b/trunk/research/players/srs_chat.html index 220f6f19f..6591c1492 100644 --- a/trunk/research/players/srs_chat.html +++ b/trunk/research/players/srs_chat.html @@ -29,6 +29,7 @@
  • VLC播放器
  • +
  • SRS-GB28181
  • diff --git a/trunk/research/players/srs_gb28181.html b/trunk/research/players/srs_gb28181.html new file mode 100644 index 000000000..b2094fa4c --- /dev/null +++ b/trunk/research/players/srs_gb28181.html @@ -0,0 +1,1172 @@ + + + + SRS + + + + + + + +
    +
    +
    + +

    Usage:

    +

    + 请点击下面的图标,启用Flash +

    +

    + 若没有见到这个图标,Chrome浏览器请打开 + chrome://settings/content/flash 并修改为"Ask first"。 +

    +
    +
    +
    +
    + +
    + API地址与端口 + +

    + +
    + +
    +
    +
    + sip会话(需内部启用sip) +
    +
      +
      + + +
      +
      + + + + + +
      + 当前会话: +
      +
      
      +                  
      +
      +
      +
      +
      +

      + +
      +
      +
      + GB28181媒体通道 +
      +
        +
        + +
        +
        + + + +
        + 当前通道: +
        + + + +
        +
        + + + 需要后台配置rtc功能才能正常启用, rtc配置请参考如下 + https://github.com/ossrs/srs/issues/307#issuecomment-602193458 +
        +
        +
        
        +                  
        +
        +
        +
        +
        + +
        + + + + + + + +
        + + +
        + + + + + + + + + + + + + + diff --git a/trunk/research/players/srs_player.html b/trunk/research/players/srs_player.html index eb50aacc9..2f5a2c4b8 100755 --- a/trunk/research/players/srs_player.html +++ b/trunk/research/players/srs_player.html @@ -40,6 +40,7 @@
      • VLC播放器
      • +
      • SRS-GB28181
      • diff --git a/trunk/research/players/srs_publisher.html b/trunk/research/players/srs_publisher.html index a649b51bd..e07467b17 100644 --- a/trunk/research/players/srs_publisher.html +++ b/trunk/research/players/srs_publisher.html @@ -29,6 +29,7 @@
      • VLC播放器
      • +
      • SRS-GB28181
      • diff --git a/trunk/research/players/srs_publisher2.html b/trunk/research/players/srs_publisher2.html index 04836a2af..bf75f3983 100644 --- a/trunk/research/players/srs_publisher2.html +++ b/trunk/research/players/srs_publisher2.html @@ -25,6 +25,7 @@
      • VLC播放器
      • +
      • SRS-GB28181
      • diff --git a/trunk/research/players/vlc.html b/trunk/research/players/vlc.html index 66591f4be..8dbf5e47c 100644 --- a/trunk/research/players/vlc.html +++ b/trunk/research/players/vlc.html @@ -26,6 +26,7 @@
      • VLC播放器
      • +
      • SRS-GB28181
      • From 33b91cd6f2b06423ef1a9a19573f36a55ec76fb6 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Thu, 16 Apr 2020 12:30:11 +0800 Subject: [PATCH 11/35] fix gb28181 api error code --- trunk/src/app/srs_app_gb28181.cpp | 85 +++++++++++++++------------ trunk/src/app/srs_app_gb28181.hpp | 18 +++--- trunk/src/app/srs_app_gb28181_sip.cpp | 78 ++++++++++++++---------- trunk/src/app/srs_app_gb28181_sip.hpp | 10 ++-- trunk/src/app/srs_app_http_api.cpp | 59 ++++++++++++++----- 5 files changed, 151 insertions(+), 99 deletions(-) diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp index 6a2fe471b..3222c2404 100644 --- a/trunk/src/app/srs_app_gb28181.cpp +++ b/trunk/src/app/srs_app_gb28181.cpp @@ -312,7 +312,12 @@ srs_error_t SrsGb28181PsRtpProcessor::on_udp_packet(const sockaddr* from, const channel.set_channel_id(tmp_id); channel.set_port_mode(RTP_PORT_MODE_FIXED); channel.set_ssrc(pkt.ssrc); - _srs_gb28181->create_stream_channel(&channel); + + srs_error_t err2 = srs_success; + if ((err2 = _srs_gb28181->create_stream_channel(&channel)) != srs_success){ + srs_warn("gb28181: RtpProcessor create stream channel error %s", srs_error_desc(err2).c_str()); + srs_error_reset(err2); + }; muxer = _srs_gb28181->fetch_rtmpmuxer(tmp_id); } @@ -1547,8 +1552,9 @@ void SrsGb28181Manger::stop_rtp_listen(std::string id) } //api -uint32_t SrsGb28181Manger::create_stream_channel(SrsGb28181StreamChannel *channel) +srs_error_t SrsGb28181Manger::create_stream_channel(SrsGb28181StreamChannel *channel) { + srs_error_t err = srs_success; srs_assert(channel); std::string id = channel->get_channel_id(); @@ -1559,15 +1565,14 @@ uint32_t SrsGb28181Manger::create_stream_channel(SrsGb28181StreamChannel *channe SrsGb28181StreamChannel s = muxer->get_channel(); channel->copy(&s); //return ERROR_GB28181_SESSION_IS_EXIST; - return ERROR_SUCCESS; + return err; } //create on rtmp muxer, gb28181 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_GB28181_CREATER_RTMPMUXER_FAILED; + return err; } //Start RTP listening port, receive gb28181 stream, @@ -1584,21 +1589,19 @@ uint32_t SrsGb28181Manger::create_stream_channel(SrsGb28181StreamChannel *channe if (port_mode == RTP_PORT_MODE_RANDOM){ alloc_port(&rtp_port); if (rtp_port <= 0){ - return ERROR_GB28181_RTP_PORT_FULL; + return srs_error_new(ERROR_GB28181_RTP_PORT_FULL, "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_GB28181_CREATER_RTMPMUXER_FAILED; + return err; } } else if(port_mode == RTP_PORT_MODE_FIXED) { rtp_port = config->rtp_mux_port; } else{ - return ERROR_GB28181_PORT_MODE_INVALID; + return srs_error_new(ERROR_GB28181_PORT_MODE_INVALID, "gb28181: port mode invalid"); } uint32_t ssrc = channel->get_ssrc(); @@ -1673,11 +1676,13 @@ uint32_t SrsGb28181Manger::create_stream_channel(SrsGb28181StreamChannel *channe muxer->copy_channel(channel); - return ERROR_SUCCESS; + return err; } -uint32_t SrsGb28181Manger::delete_stream_channel(std::string id) +srs_error_t SrsGb28181Manger::delete_stream_channel(std::string id) { + srs_error_t err = srs_success; + //notify the device to stop streaming //if an internal sip service controlled channel notify_sip_bye(id, id); @@ -1686,19 +1691,21 @@ uint32_t SrsGb28181Manger::delete_stream_channel(std::string id) if (muxer){ stop_rtp_listen(id); muxer->stop(); - return ERROR_SUCCESS; + return err; }else { - return ERROR_GB28181_SESSION_IS_NOTEXIST; + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "stream channel is not exists"); } } -uint32_t SrsGb28181Manger::query_stream_channel(std::string id, SrsJsonArray* arr) +srs_error_t SrsGb28181Manger::query_stream_channel(std::string id, SrsJsonArray* arr) { + srs_error_t err = srs_success; + if (!id.empty()){ SrsGb28181RtmpMuxer *muxer = fetch_rtmpmuxer(id); if (!muxer){ - return ERROR_GB28181_SESSION_IS_NOTEXIST; + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "stream channel not exists"); } SrsJsonObject* obj = SrsJsonAny::object(); arr->append(obj); @@ -1713,13 +1720,15 @@ uint32_t SrsGb28181Manger::query_stream_channel(std::string id, SrsJsonArray* ar } } - return ERROR_SUCCESS; + return err; } -uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc, std::string chid) +srs_error_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc, std::string chid) { + srs_error_t err = srs_success; + if (!sip_service){ - return ERROR_GB28181_SIP_NOT_RUN; + return srs_error_new(ERROR_GB28181_SIP_NOT_RUN, "sip not run"); } //if RTMP Muxer does not exist, you need to create @@ -1732,9 +1741,9 @@ uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int //channel not exist SrsGb28181StreamChannel channel; channel.set_channel_id(key); - int code = create_stream_channel(&channel); - if (code != ERROR_SUCCESS){ - return code; + err = create_stream_channel(&channel); + if (err != srs_success){ + return err; } ip = channel.get_ip(); @@ -1754,10 +1763,10 @@ uint32_t SrsGb28181Manger::notify_sip_invite(std::string id, std::string ip, int return sip_service->send_invite(&req, ip, port, ssrc, chid); } -uint32_t SrsGb28181Manger::notify_sip_bye(std::string id, std::string chid) +srs_error_t SrsGb28181Manger::notify_sip_bye(std::string id, std::string chid) { if (!sip_service){ - return ERROR_GB28181_SIP_NOT_RUN; + return srs_error_new(ERROR_GB28181_SIP_NOT_RUN, "sip not run"); } SrsSipRequest req; @@ -1765,10 +1774,10 @@ uint32_t SrsGb28181Manger::notify_sip_bye(std::string id, std::string chid) return sip_service->send_bye(&req, chid); } -uint32_t SrsGb28181Manger::notify_sip_raw_data(std::string id, std::string data) +srs_error_t SrsGb28181Manger::notify_sip_raw_data(std::string id, std::string data) { if (!sip_service){ - return ERROR_GB28181_SIP_NOT_RUN; + return srs_error_new(ERROR_GB28181_SIP_NOT_RUN, "sip not run"); } SrsSipRequest req; @@ -1777,21 +1786,19 @@ uint32_t SrsGb28181Manger::notify_sip_raw_data(std::string id, std::string data) } -uint32_t SrsGb28181Manger::notify_sip_unregister(std::string id) +srs_error_t SrsGb28181Manger::notify_sip_unregister(std::string id) { if (!sip_service){ - return ERROR_GB28181_SIP_NOT_RUN; + return srs_error_new(ERROR_GB28181_SIP_NOT_RUN, "sip not run"); } - - delete_stream_channel(id); sip_service->remove_session(id); - return ERROR_SUCCESS; + return delete_stream_channel(id); } -uint32_t SrsGb28181Manger::notify_sip_query_catalog(std::string id) +srs_error_t SrsGb28181Manger::notify_sip_query_catalog(std::string id) { - if (!sip_service){ - return ERROR_GB28181_SIP_NOT_RUN; + if (!sip_service){ + return srs_error_new(ERROR_GB28181_SIP_NOT_RUN, "sip not run"); } SrsSipRequest req; @@ -1799,10 +1806,10 @@ uint32_t SrsGb28181Manger::notify_sip_query_catalog(std::string id) return sip_service->send_query_catalog(&req); } -uint32_t SrsGb28181Manger::query_sip_session(std::string id, SrsJsonArray* arr) +srs_error_t SrsGb28181Manger::query_sip_session(std::string id, SrsJsonArray* arr) { - if (!sip_service){ - return ERROR_GB28181_SIP_NOT_RUN; + if (!sip_service){ + return srs_error_new(ERROR_GB28181_SIP_NOT_RUN, "sip not run"); } return sip_service->query_sip_session(id, arr); diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 9a4607c59..777e334eb 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -413,16 +413,16 @@ public: public: //stream channel api - uint32_t create_stream_channel(SrsGb28181StreamChannel *channel); - uint32_t delete_stream_channel(std::string id); - uint32_t query_stream_channel(std::string id, SrsJsonArray* arr); + srs_error_t create_stream_channel(SrsGb28181StreamChannel *channel); + srs_error_t delete_stream_channel(std::string id); + srs_error_t query_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, std::string chid); - uint32_t notify_sip_bye(std::string id, std::string chid); - uint32_t notify_sip_raw_data(std::string id, std::string data); - uint32_t notify_sip_unregister(std::string id); - uint32_t notify_sip_query_catalog(std::string id); - uint32_t query_sip_session(std::string id, SrsJsonArray* arr); + srs_error_t notify_sip_invite(std::string id, std::string ip, int port, uint32_t ssrc, std::string chid); + srs_error_t notify_sip_bye(std::string id, std::string chid); + srs_error_t notify_sip_raw_data(std::string id, std::string data); + srs_error_t notify_sip_unregister(std::string id); + srs_error_t notify_sip_query_catalog(std::string id); + srs_error_t query_sip_session(std::string id, SrsJsonArray* arr); private: void destroy(); diff --git a/trunk/src/app/srs_app_gb28181_sip.cpp b/trunk/src/app/srs_app_gb28181_sip.cpp index 9348a35e2..1c7db07c6 100644 --- a/trunk/src/app/srs_app_gb28181_sip.cpp +++ b/trunk/src/app/srs_app_gb28181_sip.cpp @@ -201,20 +201,25 @@ srs_error_t SrsGb28181SipSession::do_cycle() } //create stream channel, ready for recv device av stream - int code = _srs_gb28181->create_stream_channel(&ch); + srs_error_t err = _srs_gb28181->create_stream_channel(&ch); - if (code == ERROR_SUCCESS){ + if ((err = _srs_gb28181->create_stream_channel(&ch)) == srs_success){ SrsSipRequest req; req.sip_auth_id = _session_id; //send invite to device, req push av stream - code = servcie->send_invite(&req, ch.get_ip(), - ch.get_rtp_port(), ch.get_ssrc(), chid); - - //the same device can't be sent too fast. the device can't handle it - srs_usleep(1*SRS_UTIME_SECONDS); + err = servcie->send_invite(&req, ch.get_ip(), + ch.get_rtp_port(), ch.get_ssrc(), chid); } - + + int code = srs_error_code(err); + if (err != srs_success){ + srs_error_reset(err); + } + + //the same device can't be sent too fast. the device can't handle it + srs_usleep(1*SRS_UTIME_SECONDS); + srs_trace("gb28181: %s clients device=%s send invite code=%d", _session_id.c_str(), chid.c_str(), code); }//end for (it) @@ -238,9 +243,14 @@ srs_error_t SrsGb28181SipSession::do_cycle() query_duration >= config->sip_query_catalog_interval) { SrsSipRequest req; req.sip_auth_id = _session_id; - servcie->send_query_catalog(&req); _query_catalog_time = srs_get_system_time(); + srs_error_t err = servcie->send_query_catalog(&req); + if (err != srs_success){ + srs_trace("gb28181: sip query catalog error %s",srs_error_desc(err).c_str()); + srs_error_reset(err); + } + //print device status srs_trace("gb28181: sip session=%s peer(%s, %d) status(%s,%s) duration(%u,%u)", _session_id.c_str(), _peer_ip.c_str(), _peer_port, @@ -621,26 +631,28 @@ int SrsGb28181SipService::send_status(SrsSipRequest *req, sockaddr *f, int l) } -int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, uint32_t ssrc, std::string chid) +srs_error_t SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, uint32_t ssrc, std::string chid) { + srs_error_t err = srs_success; + srs_assert(req); SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); if (!sip_session){ - return ERROR_GB28181_SESSION_IS_NOTEXIST; + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "sip session not exist"); } //if you are inviting or succeed in invite, //you cannot invite again. you need to 'bye' and try again SrsGb28181Device *device = sip_session->get_device_info(chid); if (!device || device->device_status != "ON"){ - return ERROR_GB28181_SIP_CH_OFFLINE; + return srs_error_new(ERROR_GB28181_SIP_CH_OFFLINE, "sip device channel offline"); } if (device->invite_status == SrsGb28181SipSessionTrying || device->invite_status == SrsGb28181SipSessionInviteOk){ - return ERROR_GB28181_SIP_IS_INVITING; + return srs_error_new(ERROR_GB28181_SIP_IS_INVITING, "sip device channel inviting"); } req->host = config->host; @@ -656,7 +668,7 @@ int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, if (send_message(&addr, sip_session->sockaddr_fromlen(), ss) <= 0) { - return ERROR_GB28181_SIP_INVITE_FAILED; + return srs_error_new(ERROR_GB28181_SIP_INVITE_FAILED, "sip device invite failed"); } //prame branch, from_tag, to_tag, call_id, @@ -668,27 +680,25 @@ int SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, int port, //call_id map sip_session sip_session_map_by_callid(sip_session, req->call_id); - return ERROR_SUCCESS; + return err; } -int SrsGb28181SipService::send_bye(SrsSipRequest *req, std::string chid) +srs_error_t SrsGb28181SipService::send_bye(SrsSipRequest *req, std::string chid) { + srs_error_t err = srs_success; + srs_assert(req); SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); if (!sip_session){ - return ERROR_GB28181_SESSION_IS_NOTEXIST; + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "sip session not exist"); } SrsGb28181Device *device = sip_session->get_device_info(chid); if (!device){ - return ERROR_GB28181_SIP_CH_NOTEXIST; + return srs_error_new(ERROR_GB28181_SIP_CH_NOTEXIST, "sip device channel not exist"); } - // if (status == SrsGb28181SipSessionTrying || - // status == SrsGb28181SipSessionInviteOk){ - // return ERROR_GB28181_SIP_IS_INVITING; - // } //prame branch, from_tag, to_tag, call_id, //The parameter of 'bye' must be the same as 'invite' @@ -709,20 +719,22 @@ int SrsGb28181SipService::send_bye(SrsSipRequest *req, std::string chid) sockaddr addr = sip_session->sockaddr_from(); if (send_message(&addr, sip_session->sockaddr_fromlen(), ss) <= 0) { - return ERROR_GB28181_SIP_BYE_FAILED; + return srs_error_new(ERROR_GB28181_SIP_BYE_FAILED, "sip bye failed"); } - return ERROR_SUCCESS; + return err; } -int SrsGb28181SipService::send_sip_raw_data(SrsSipRequest *req, std::string data) +srs_error_t SrsGb28181SipService::send_sip_raw_data(SrsSipRequest *req, std::string data) { + srs_error_t err = srs_success; + srs_assert(req); SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); if (!sip_session){ - return ERROR_GB28181_SESSION_IS_NOTEXIST; + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "sip session no exist"); } std::stringstream ss; @@ -731,13 +743,13 @@ int SrsGb28181SipService::send_sip_raw_data(SrsSipRequest *req, std::string dat sockaddr addr = sip_session->sockaddr_from(); if (send_message(&addr, sip_session->sockaddr_fromlen(), ss) <= 0) { - return ERROR_GB28181_SIP_RAW_DATA_FAILED; + return srs_error_new(ERROR_GB28181_SIP_RAW_DATA_FAILED, "sip raw data failed"); } - return ERROR_SUCCESS; + return err; } -int SrsGb28181SipService::send_query_catalog(SrsSipRequest *req) +srs_error_t SrsGb28181SipService::send_query_catalog(SrsSipRequest *req) { req->host = config->host; req->host_port = config->sip_port; @@ -752,12 +764,14 @@ int SrsGb28181SipService::send_query_catalog(SrsSipRequest *req) return send_sip_raw_data(req, ss.str()); } -int SrsGb28181SipService::query_sip_session(std::string sid, SrsJsonArray* arr) +srs_error_t SrsGb28181SipService::query_sip_session(std::string sid, SrsJsonArray* arr) { + srs_error_t err = srs_success; + if (!sid.empty()){ SrsGb28181SipSession* sess = fetch(sid); if (!sess){ - return ERROR_GB28181_SESSION_IS_NOTEXIST; + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "sip session not exist"); } SrsJsonObject* obj = SrsJsonAny::object(); arr->append(obj); @@ -772,7 +786,7 @@ int SrsGb28181SipService::query_sip_session(std::string sid, SrsJsonArray* arr) } } - return ERROR_SUCCESS; + return err; } srs_error_t SrsGb28181SipService::fetch_or_create_sip_session(SrsSipRequest *req, SrsGb28181SipSession** sip_session) diff --git a/trunk/src/app/srs_app_gb28181_sip.hpp b/trunk/src/app/srs_app_gb28181_sip.hpp index df30b254f..952fd7a3c 100644 --- a/trunk/src/app/srs_app_gb28181_sip.hpp +++ b/trunk/src/app/srs_app_gb28181_sip.hpp @@ -172,9 +172,9 @@ public: 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, std::string chid); - int send_bye(SrsSipRequest *req, std::string chid); - int send_query_catalog(SrsSipRequest *req); + srs_error_t send_invite(SrsSipRequest *req, std::string ip, int port, uint32_t ssrc, std::string chid); + srs_error_t send_bye(SrsSipRequest *req, std::string chid); + srs_error_t send_query_catalog(SrsSipRequest *req); // The SIP command is transmitted through HTTP API, // and the body content is transmitted to the device, @@ -190,8 +190,8 @@ public: // Content-Length: 0 // // - int send_sip_raw_data(SrsSipRequest *req, std::string data); - int query_sip_session(std::string sid, SrsJsonArray* arr); + srs_error_t send_sip_raw_data(SrsSipRequest *req, std::string data); + srs_error_t query_sip_session(std::string sid, SrsJsonArray* arr); public: srs_error_t fetch_or_create_sip_session(SrsSipRequest *req, SrsGb28181SipSession** sess); diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 63c2af30a..d0be9042c 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1670,6 +1670,8 @@ SrsGoApiGb28181::~SrsGoApiGb28181() srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { + srs_error_t err = srs_success; + SrsJsonObject* obj = SrsJsonAny::object(); SrsAutoFree(SrsJsonObject, obj); @@ -1696,9 +1698,9 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa 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) { + + if ((err =_srs_gb28181->create_stream_channel(&channel)) != srs_success) { + int code = srs_error_code(err); srs_error_reset(err); return srs_api_response_code(w, r, code); } @@ -1718,15 +1720,20 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); } - uint32_t code = _srs_gb28181->delete_stream_channel(id); + err =_srs_gb28181->delete_stream_channel(id); + int code = srs_error_code(err); + if (err != srs_success) { + srs_error_reset(err); + } + 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->query_stream_channel(id, arr); - if (code != ERROR_SUCCESS) { + if ((err = _srs_gb28181->query_stream_channel(id, arr)) != srs_success) { + int code = srs_error_code(err); srs_error_reset(err); return srs_api_response_code(w, r, code); } @@ -1745,9 +1752,12 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa int _port = strtoul(rtp_port.c_str(), NULL, 10); uint32_t _ssrc = (uint32_t)(strtoul(ssrc.c_str(), NULL, 10)); - + err = _srs_gb28181->notify_sip_invite(id, ip, _port, _ssrc, chid); + int code = srs_error_code(err); + if (err != srs_success) { + srs_error_reset(err); + } - int code = _srs_gb28181->notify_sip_invite(id, ip, _port, _ssrc, chid); return srs_api_response_code(w, r, code); } else if(action == "sip_bye"){ @@ -1756,7 +1766,12 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); } - int code = _srs_gb28181->notify_sip_bye(id, chid); + err = _srs_gb28181->notify_sip_bye(id, chid); + int code = srs_error_code(err); + if (err != srs_success) { + srs_error_reset(err); + } + return srs_api_response_code(w, r, code); } else if(action == "sip_raw_data"){ @@ -1766,7 +1781,13 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa std::string body; r->body_read_all(body); - int code = _srs_gb28181->notify_sip_raw_data(id, body); + + err = _srs_gb28181->notify_sip_raw_data(id, body); + int code = srs_error_code(err); + if (err != srs_success) { + srs_error_reset(err); + } + return srs_api_response_code(w, r, code); } else if(action == "sip_unregister"){ @@ -1774,7 +1795,12 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); } - int code = _srs_gb28181->notify_sip_unregister(id); + err = _srs_gb28181->notify_sip_unregister(id); + int code = srs_error_code(err); + if (err != srs_success) { + srs_error_reset(err); + } + return srs_api_response_code(w, r, code); } else if(action == "sip_query_catalog"){ @@ -1782,15 +1808,20 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); } - int code = _srs_gb28181->notify_sip_query_catalog(id); + err = _srs_gb28181->notify_sip_query_catalog(id); + int code = srs_error_code(err); + if (err != srs_success) { + srs_error_reset(err); + } + return srs_api_response_code(w, r, code); } else if(action == "sip_query_session"){ SrsJsonArray* arr = SrsJsonAny::array(); data->set("sessions", arr); - uint32_t code = _srs_gb28181->query_sip_session(id, arr); - if (code != ERROR_SUCCESS) { + if ((err = _srs_gb28181->query_sip_session(id, arr)) != srs_success) { + int code = srs_error_code(err); srs_error_reset(err); return srs_api_response_code(w, r, code); } From b391ed62065b7b374900c782662290e0e1b847e9 Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Thu, 16 Apr 2020 13:08:42 +0800 Subject: [PATCH 12/35] fix gb28281 html rtc player stop --- trunk/research/players/srs_gb28181.html | 140 ++++++++++++------------ 1 file changed, 71 insertions(+), 69 deletions(-) diff --git a/trunk/research/players/srs_gb28181.html b/trunk/research/players/srs_gb28181.html index b2094fa4c..7d3a82d0f 100644 --- a/trunk/research/players/srs_gb28181.html +++ b/trunk/research/players/srs_gb28181.html @@ -123,8 +123,9 @@
        - 需要后台配置rtc功能才能正常启用, rtc配置请参考如下 - https://github.com/ossrs/srs/issues/307#issuecomment-602193458 +
        RTC播放,需要后台启用RTC功能才能正常播放, + RTC配置参考 +
        
        @@ -1090,82 +1091,83 @@
                         startPlay($("#txt_rtc_url").val());
                     });
         
        +
        +            $("#rtc_player_modal").on("hide", function(){
        +                console.log("rtc stop");
        +                if (pc) {
        +                    pc.close();
        +                 }
        +             });
        +
                     var startPlay = function(url) {
        -            $('#rtc_media_player').show();
        -            var urlObject = parse_rtmp_url(url);
        -            var schema = window.location.protocol;
        +                    $('#rtc_media_player').show();
        +                    var urlObject = parse_rtmp_url(url);
        +                    var schema = window.location.protocol;
         
        -            // Close PC when user replay.
        -            if (pc) {
        -                pc.close();
        -            }
        -
        -         
        -
        -            pc = new RTCPeerConnection(null);
        -            pc.onaddstream = function (event) {
        -                console.debug(event.stream);
        -                $('#rtc_media_player').prop('srcObject', event.stream);
        -            };
        -            new Promise(function(resolve, reject) {
        -                pc.addTransceiver("audio", {direction: "recvonly"});
        -                pc.addTransceiver("video", {direction: "recvonly"});
        -
        -                pc.createOffer(function(offer){
        -                    resolve(offer);
        -                },function(reason){
        -                    reject(reason);
        -                });
        -            }).then(function(offer) {
        -                return pc.setLocalDescription(offer).then(function(){ return offer; });
        -            }).then(function(offer) {
        -                return new Promise(function(resolve, reject) {
        -                    var port = urlObject.port || 1985;
        -
        -                    // @see https://github.com/rtcdn/rtcdn-draft
        -                    var api = urlObject.user_query.play || '/rtc/v1/play/';
        -                    if (api.lastIndexOf('/') != api.length - 1) {
        -                        api += '/';
        +                    // Close PC when user replay.
        +                    if (pc) {
        +                        pc.close();
                             }
         
        -                    var url = schema + '//' + urlObject.server + ':' + port + api;
        -                    for (var key in urlObject.user_query) {
        -                        if (key != 'api' && key != 'play') {
        -                            url += '&' + key + '=' + urlObject.user_query[key];
        -                        }
        -                    }
        -                    // Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
        -                    url = url.replace(api + '&', api + '?');
        -
        -                    // @see https://github.com/rtcdn/rtcdn-draft
        -                    var data = {
        -                        api: url, streamurl: urlObject.url, clientip: null, sdp: offer.sdp
        +                    pc = new RTCPeerConnection(null);
        +                    pc.onaddstream = function (event) {
        +                        console.debug(event.stream);
        +                        $('#rtc_media_player').prop('srcObject', event.stream);
                             };
        -                    console.log("offer: " + JSON.stringify(data));
        +                    new Promise(function(resolve, reject) {
        +                        pc.addTransceiver("audio", {direction: "recvonly"});
        +                        pc.addTransceiver("video", {direction: "recvonly"});
         
        -                    $.ajax({
        -                        type: "POST", url: url, data: JSON.stringify(data),
        -                        contentType:'application/json', dataType: 'json'
        -                    }).done(function(data) {
        -                        console.log("answer: " + JSON.stringify(data));
        -                        resolve(data.sdp);
        -                    }).fail(function(reason){
        -                        reject(reason);
        +                        pc.createOffer(function(offer){
        +                            resolve(offer);
        +                        },function(reason){
        +                            reject(reason);
        +                        });
        +                    }).then(function(offer) {
        +                        return pc.setLocalDescription(offer).then(function(){ return offer; });
        +                    }).then(function(offer) {
        +                        return new Promise(function(resolve, reject) {
        +                            var port = urlObject.port || 1985;
        +
        +                            // @see https://github.com/rtcdn/rtcdn-draft
        +                            var api = urlObject.user_query.play || '/rtc/v1/play/';
        +                            if (api.lastIndexOf('/') != api.length - 1) {
        +                                api += '/';
        +                            }
        +
        +                            var url = schema + '//' + urlObject.server + ':' + port + api;
        +                            for (var key in urlObject.user_query) {
        +                                if (key != 'api' && key != 'play') {
        +                                    url += '&' + key + '=' + urlObject.user_query[key];
        +                                }
        +                            }
        +                            // Replace /rtc/v1/play/&k=v to /rtc/v1/play/?k=v
        +                            url = url.replace(api + '&', api + '?');
        +
        +                            // @see https://github.com/rtcdn/rtcdn-draft
        +                            var data = {
        +                                api: url, streamurl: urlObject.url, clientip: null, sdp: offer.sdp
        +                            };
        +                            console.log("offer: " + JSON.stringify(data));
        +
        +                            $.ajax({
        +                                type: "POST", url: url, data: JSON.stringify(data),
        +                                contentType:'application/json', dataType: 'json'
        +                            }).done(function(data) {
        +                                console.log("answer: " + JSON.stringify(data));
        +                                resolve(data.sdp);
        +                            }).fail(function(reason){
        +                                reject(reason);
        +                            });
        +                        });
        +                    }).then(function(answer) {
        +                        return pc.setRemoteDescription(new RTCSessionDescription({type: 'answer', sdp: answer}));
        +                    }).catch(function(reason) {
        +                        throw reason;
                             });
        -                });
        -            }).then(function(answer) {
        -                return pc.setRemoteDescription(new RTCSessionDescription({type: 'answer', sdp: answer}));
        -            }).catch(function(reason) {
        -                throw reason;
        -            });
        -        };
        -
        -  
        -            
        +                };
                 }
         
        -
        -
                 apply_url_change();
             };
         
        
        From 06412ddddc2d00616d426eee76626a93bc9b8e97 Mon Sep 17 00:00:00 2001
        From: kyxlx550 
        Date: Thu, 16 Apr 2020 18:33:43 +0800
        Subject: [PATCH 13/35] fix gb28281 html ret error show
        
        ---
         trunk/research/players/srs_gb28181.html | 17 ++++++++++++-----
         1 file changed, 12 insertions(+), 5 deletions(-)
        
        diff --git a/trunk/research/players/srs_gb28181.html b/trunk/research/players/srs_gb28181.html
        index 7d3a82d0f..2962b4d4a 100644
        --- a/trunk/research/players/srs_gb28181.html
        +++ b/trunk/research/players/srs_gb28181.html
        @@ -976,7 +976,9 @@
         
                             $('#sipSessionMessage').html(syntaxHighlight(ret)); 
         
        -                    $("#btn_sip_query_session").click();
        +                    if (ret != undefined && ret.code == 0){
        +                      $("#btn_sip_query_session").click();
        +                    }
                         }
                     });
         
        @@ -993,7 +995,9 @@
                             var ret = http_get(apiurl);
                             $('#sipSessionMessage').html(syntaxHighlight(ret)); 
         
        -                    $("#btn_sip_query_session").click();
        +                    if (ret != undefined && ret.code == 0){
        +                      $("#btn_sip_query_session").click();
        +                    }
                         }
                     });
         
        @@ -1010,7 +1014,9 @@
                             var ret = http_get(apiurl);
                             $('#sipSessionMessage').html(syntaxHighlight(ret)); 
         
        -                    $("#btn_sip_query_session").click();
        +                    if (ret != undefined && ret.code == 0){
        +                      $("#btn_sip_query_session").click();
        +                    }
                         }
                     });
         
        @@ -1026,8 +1032,9 @@
                             var apiurl = url + "/api/v1/gb28181?action=sip_query_catalog&id=" + id + "&chid="+chid;
                             var ret = http_get(apiurl);
                             $('#sipSessionMessage').html(syntaxHighlight(ret)); 
        -
        -                    $("#btn_sip_query_session").click();
        +                    if (ret != undefined && ret.code == 0){
        +                      $("#btn_sip_query_session").click();
        +                    }
                         }
                     });
         
        
        From 98c29b2b9a4d9945d57c71a52fff2835942e6a74 Mon Sep 17 00:00:00 2001
        From: kyxlx550 
        Date: Fri, 17 Apr 2020 00:17:47 +0800
        Subject: [PATCH 14/35] fix sip stack param check error
        
        ---
         trunk/src/protocol/srs_sip_stack.cpp | 2 +-
         1 file changed, 1 insertion(+), 1 deletion(-)
        
        diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp
        index e29fc4876..9b8ac1e4b 100644
        --- a/trunk/src/protocol/srs_sip_stack.cpp
        +++ b/trunk/src/protocol/srs_sip_stack.cpp
        @@ -109,7 +109,7 @@ std::string srs_sip_get_param(std::string msg, std::string param)
         
                 std::vector  v_pram = srs_string_split(value, "=");
                 
        -        if (v_pram.size() > 0) {
        +        if (v_pram.size() > 1) {
                     return v_pram.at(1);
                 }
             }
        
        From fb23739113f7a169247a1cfb9aa9c41b247007b8 Mon Sep 17 00:00:00 2001
        From: kyxlx550 
        Date: Fri, 17 Apr 2020 17:03:03 +0800
        Subject: [PATCH 15/35] fix use gb28181 log id
        
        ---
         trunk/src/app/srs_app_gb28181.cpp | 2 +-
         1 file changed, 1 insertion(+), 1 deletion(-)
        
        diff --git a/trunk/src/app/srs_app_gb28181.cpp b/trunk/src/app/srs_app_gb28181.cpp
        index 3222c2404..d0e29d464 100644
        --- a/trunk/src/app/srs_app_gb28181.cpp
        +++ b/trunk/src/app/srs_app_gb28181.cpp
        @@ -329,7 +329,7 @@ srs_error_t SrsGb28181PsRtpProcessor::on_udp_packet(const sockaddr* from, const
                         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",
        +                    srs_warn("<- " SRS_CONSTS_LOG_GB28181_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 {
        
        From 167711400a058492de546df9f6d1bd307fe806f5 Mon Sep 17 00:00:00 2001
        From: kyxlx550 
        Date: Sun, 19 Apr 2020 00:18:30 +0800
        Subject: [PATCH 16/35] fix cascade SIP bye failed
        
        ---
         trunk/src/app/srs_app_gb28181_sip.cpp | 31 +++++++++++--
         trunk/src/protocol/srs_sip_stack.cpp  | 65 +++++++++++++++++++++++----
         trunk/src/protocol/srs_sip_stack.hpp  |  3 ++
         3 files changed, 86 insertions(+), 13 deletions(-)
        
        diff --git a/trunk/src/app/srs_app_gb28181_sip.cpp b/trunk/src/app/srs_app_gb28181_sip.cpp
        index 1c7db07c6..36616cd4f 100644
        --- a/trunk/src/app/srs_app_gb28181_sip.cpp
        +++ b/trunk/src/app/srs_app_gb28181_sip.cpp
        @@ -472,7 +472,8 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port,
                     return err;
                 }
                 srs_assert(sip_session);
        -        
        +        sip_session->set_request(req);
        +
                 send_status(req, from, fromlen);
                 sip_session->set_register_status(SrsGb28181SipSessionRegisterOk);
                 sip_session->set_register_time(srs_get_system_time());
        @@ -481,6 +482,7 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port,
                 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){
        @@ -535,8 +537,15 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port,
                             device->req_inivate.copy(req);
                             device->invite_time = srs_get_system_time();
                         }
        +            }else if (req->status == "100") {
        +                //send_ack(req, from, fromlen);
        +                SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id);
        +                if (device){
        +                    device->req_inivate.copy(req);
        +                    device->invite_status = SrsGb28181SipSessionTrying;
        +                    device->invite_time = srs_get_system_time();
        +                }
                     }else{
        -                send_ack(req, from, fromlen);
                         SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id);
                         if (device){
                             device->req_inivate.copy(req);
        @@ -569,7 +578,13 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port,
                     srs_trace("gb28181: BYE  %s client status=%s", req->sip_auth_id.c_str(), req->status.c_str());
         
                     if (req->status == "200") {
        -                srs_trace("gb28181: BYE response %s client status=%s", req->sip_auth_id.c_str(), req->status.c_str());
        +                SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id);
        +                if (device){
        +                    device->invite_status = SrsGb28181SipSessionBye;
        +                    device->invite_time = srs_get_system_time();
        +                }
        +            }else {
        +                //TODO:fixme
                         SrsGb28181Device *device = sip_session->get_device_info(req->sip_auth_id);
                         if (device){
                             device->invite_status = SrsGb28181SipSessionBye;
        @@ -661,6 +676,10 @@ srs_error_t  SrsGb28181SipService::send_invite(SrsSipRequest *req,  string ip, i
             req->serial = config->sip_serial;
             req->chid = chid;
         
        +    SrsSipRequest register_req = sip_session->request();
        +    req->to_realm = register_req.to_realm;
        +    req->from_realm = config->sip_realm;
        +   
             std::stringstream ss;
             sip->req_invite(ss, req, ip, port, ssrc);
            
        @@ -711,7 +730,11 @@ srs_error_t SrsGb28181SipService::send_bye(SrsSipRequest *req, std::string chid)
             req->realm = config->sip_realm;
             req->serial = config->sip_serial;
             req->chid = chid;
        -
        +    
        +    SrsSipRequest register_req = sip_session->request();
        +    req->to_realm = register_req.to_realm;
        +    req->from_realm = config->sip_realm;
        +   
             //get protocol stack 
             std::stringstream ss;
             sip->req_bye(ss, req);
        diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp
        index 9b8ac1e4b..466c69b2e 100644
        --- a/trunk/src/protocol/srs_sip_stack.cpp
        +++ b/trunk/src/protocol/srs_sip_stack.cpp
        @@ -158,6 +158,9 @@ SrsSipRequest::SrsSipRequest()
             peer_port = 0;
         
             chid = "";
        +
        +    from_realm = "";
        +    to_realm = "";
         }
         
         SrsSipRequest::~SrsSipRequest()
        @@ -247,6 +250,9 @@ void SrsSipRequest::copy(SrsSipRequest* src)
         
             xml_body_map = src->xml_body_map;
             device_list_map = src->device_list_map;
        +
        +    from_realm = src->from_realm;
        +    to_realm  = src->to_realm;
         }
         
         SrsSipStack::SrsSipStack()
        @@ -490,6 +496,11 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m
                                     if (srs_string_contains(content, "tag")) {
                                         req->from_tag = srs_sip_get_param(content.c_str(), "tag");
                                     }
        +
        +                            std::vector vec = srs_string_split(req->from, "@");
        +                            if (vec.size() > 1){
        +                                req->from_realm = vec.at(1);
        +                            }
                                 } 
                                 else if (!strcasecmp(phead, "to:")) {
                                     content = srs_string_replace(content, "sip:", "");
        @@ -497,6 +508,11 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m
                                     if (srs_string_contains(content, "tag")) {
                                         req->to_tag = srs_sip_get_param(content.c_str(), "tag");
                                     }
        +
        +                            std::vector vec = srs_string_split(req->to, "@");
        +                            if (vec.size() > 1){
        +                                req->to_realm = vec.at(1);
        +                            }
                                 } 
                                 else if (!strcasecmp(phead, "via:")) {
                                     req->via = content;
        @@ -579,7 +595,12 @@ srs_error_t SrsSipStack::do_parse_request(SrsSipRequest* req, const char* recv_m
                          
                         //map key:devicd_id value:status 
                         for(int i=0 ; idevice_list_map[vec_device_id.at(i)] = vec_device_status.at(i);
        +                    std::string status = "";
        +                    if (vec_device_id.size() > i) {
        +                        status = vec_device_status.at(i);
        +                    }
        +              
        +                    req->device_list_map[vec_device_id.at(i)] = status;
                         }
                     }else{
                         //TODO: fixme
        @@ -651,7 +672,7 @@ std::string SrsSipStack::get_sip_via(SrsSipRequest const *req)
             std::vector vec_ip_port = srs_string_split(ip_port, ":");
         
             std::string ip = vec_ip_port.empty() ? "" : vec_ip_port.at(0);
        -    std::string port = vec_ip_port.size() > 0 ? vec_ip_port.at(1) : "";
        +    std::string port = vec_ip_port.size() > 1 ? vec_ip_port.at(1) : "";
             
             std::string branch, rport, received;
             if (req->branch.empty()){
        @@ -660,6 +681,14 @@ std::string SrsSipStack::get_sip_via(SrsSipRequest const *req)
                 branch = ";branch=" + req->branch;
             }
         
        +    if (!req->peer_ip.empty()){
        +        ip = req->peer_ip;
        +
        +        std::stringstream ss;
        +        ss << req->peer_port;
        +        port = ss.str();
        +    }
        +
             received = ";received=" + ip;
             rport = ";rport=" + port;
         
        @@ -850,7 +879,7 @@ void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, in
           
             std::stringstream sdp;
             sdp << "v=0" << SRS_RTSP_CRLF
        -    << "o=" << req->chid << " 0 0 IN IP4 " << ip << SRS_RTSP_CRLF
        +    << "o=" << req->serial << " 0 0 IN IP4 " << ip << SRS_RTSP_CRLF
             << "s=Play" << SRS_RTSP_CRLF
             << "c=IN IP4 " << ip << SRS_RTSP_CRLF
             << "t=0 0" << SRS_RTSP_CRLF
        @@ -871,14 +900,23 @@ void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, in
             int rand = srs_sip_random(1000, 9999);
             std::stringstream from, to, uri, branch, from_tag, call_id;
             //"INVITE sip:34020000001320000001@3402000000 SIP/2.0\r\n
        -    uri << "sip:" <<   req->chid << "@" << req->realm;
        +    uri << "sip:" <<  req->chid << "@" << req->realm;
             //From: ;tag=500485%d\r\n
        -    from << req->serial << "@" << req->host << ":"  << req->host_port;
        +    from << req->serial << "@" << req->realm;
             to <<  req->chid <<  "@" << req->realm;
             call_id << "2020" << rand ;
         
             req->from = from.str();
        -    req->to   = to.str();
        +    req->to = to.str();
        +
        +    if (!req->to_realm.empty()){
        +        req->to  =  req->chid + "@" + req->to_realm;
        +    }
        +
        +    if (!req->from_realm.empty()){
        +        req->from  =  req->serial + "@" + req->from_realm;
        +    }
        +
             req->uri  = uri.str();
             req->call_id = call_id.str();
         
        @@ -948,8 +986,8 @@ void SrsSipStack::req_ack(std::stringstream& ss, SrsSipRequest *req){
           
             ss << "ACK " << "sip:" <<  req->chid << "@" << 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: chid <<  "@" << req->realm << ">\r\n"
        +    << "From: " << get_sip_from(req) << SRS_RTSP_CRLF
        +    << "To: "<< get_sip_to(req) << SRS_RTSP_CRLF
             << "Call-ID: " << req->call_id << SRS_RTSP_CRLF
             << "CSeq: " << req->seq << " ACK"<< SRS_RTSP_CRLF
             << "Max-Forwards: 70" << SRS_RTSP_CRLF
        @@ -989,7 +1027,16 @@ void SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req)
             to << req->chid <<  "@" <<  req->realm;
         
             req->from = from.str();
        -    req->to   = to.str();
        +    req->to = to.str();
        +
        +    if (!req->to_realm.empty()){
        +        req->to  =  req->chid + "@" + req->to_realm;
        +    }
        +
        +    if (!req->from_realm.empty()){
        +        req->from  =  req->serial + "@" + req->from_realm;
        +    }
        +
             req->uri  = uri.str();
         
             int seq = srs_sip_random(22, 99);
        diff --git a/trunk/src/protocol/srs_sip_stack.hpp b/trunk/src/protocol/srs_sip_stack.hpp
        index 1f6630429..141a289ec 100644
        --- a/trunk/src/protocol/srs_sip_stack.hpp
        +++ b/trunk/src/protocol/srs_sip_stack.hpp
        @@ -106,6 +106,9 @@ public:
             int host_port;
             SrsSipCmdType cmdtype;
         
        +    std::string from_realm;
        +    std::string to_realm;
        +
         public:
             SrsRtspSdp* sdp;
             SrsRtspTransport* transport;
        
        From a579f51e729e69586cfd993f8eb7c207c0d9a26a Mon Sep 17 00:00:00 2001
        From: winlin 
        Date: Sun, 19 Apr 2020 20:51:39 +0800
        Subject: [PATCH 17/35] Remove ST in research
        
        ---
         trunk/3rdparty/st-srs/README.md |   2 +
         trunk/research/st/Makefile      | 100 ----
         trunk/research/st/common.h      | 445 ------------------
         trunk/research/st/event.c       | 483 -------------------
         trunk/research/st/io.c          | 792 --------------------------------
         trunk/research/st/key.c         | 116 -----
         trunk/research/st/md.S          | 151 ------
         trunk/research/st/md.h          | 193 --------
         trunk/research/st/public.h      | 164 -------
         trunk/research/st/sched.c       | 680 ---------------------------
         trunk/research/st/srs.c         | 497 --------------------
         trunk/research/st/st/init       |   3 -
         trunk/research/st/st/st.upp     |  18 -
         trunk/research/st/stk.c         | 169 -------
         trunk/research/st/sync.c        | 352 --------------
         15 files changed, 2 insertions(+), 4163 deletions(-)
         delete mode 100755 trunk/research/st/Makefile
         delete mode 100644 trunk/research/st/common.h
         delete mode 100644 trunk/research/st/event.c
         delete mode 100644 trunk/research/st/io.c
         delete mode 100644 trunk/research/st/key.c
         delete mode 100755 trunk/research/st/md.S
         delete mode 100644 trunk/research/st/md.h
         delete mode 100644 trunk/research/st/public.h
         delete mode 100755 trunk/research/st/sched.c
         delete mode 100644 trunk/research/st/srs.c
         delete mode 100644 trunk/research/st/st/init
         delete mode 100755 trunk/research/st/st/st.upp
         delete mode 100644 trunk/research/st/stk.c
         delete mode 100644 trunk/research/st/sync.c
        
        diff --git a/trunk/3rdparty/st-srs/README.md b/trunk/3rdparty/st-srs/README.md
        index 522d3c761..8ea593b39 100644
        --- a/trunk/3rdparty/st-srs/README.md
        +++ b/trunk/3rdparty/st-srs/README.md
        @@ -92,5 +92,7 @@ Important cli options:
         1. About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg).
         1. About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg)
         1. About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25).
        +1. About the scheduler, read [#13-scheduler](https://github.com/ossrs/state-threads/issues/13#issuecomment-616025527).
        +1. About the IO event system, read [#13-IO](https://github.com/ossrs/state-threads/issues/13#issuecomment-616096568).
         
         Winlin 2016
        diff --git a/trunk/research/st/Makefile b/trunk/research/st/Makefile
        deleted file mode 100755
        index 0b24bb80c..000000000
        --- a/trunk/research/st/Makefile
        +++ /dev/null
        @@ -1,100 +0,0 @@
        -# The contents of this file are subject to the Mozilla Public
        -# License Version 1.1 (the "License"); you may not use this file
        -# except in compliance with the License. You may obtain a copy of
        -# the License at http://www.mozilla.org/MPL/
        -# 
        -# Software distributed under the License is distributed on an "AS
        -# IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        -# implied. See the License for the specific language governing
        -# rights and limitations under the License.
        -# 
        -# The Original Code is the Netscape Portable Runtime library.
        -# 
        -# The Initial Developer of the Original Code is Netscape
        -# Communications Corporation.  Portions created by Netscape are 
        -# Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        -# Rights Reserved.
        -# 
        -# Contributor(s):  Silicon Graphics, Inc.
        -# 
        -# Portions created by SGI are Copyright (C) 2000-2001 Silicon
        -# Graphics, Inc.  All Rights Reserved.
        -# 
        -# Alternatively, the contents of this file may be used under the
        -# terms of the GNU General Public License Version 2 or later (the
        -# "GPL"), in which case the provisions of the GPL are applicable 
        -# instead of those above.  If you wish to allow use of your 
        -# version of this file only under the terms of the GPL and not to
        -# allow others to use your version of this file under the MPL,
        -# indicate your decision by deleting the provisions above and
        -# replace them with the notice and other provisions required by
        -# the GPL.  If you do not delete the provisions above, a recipient
        -# may use your version of this file under either the MPL or the
        -# GPL.
        -
        -##########################
        -# Target dir and cc:
        -CC          = cc
        -TARGETDIR   = objs
        -
        -##########################
        -# Supported OSes:
        -OS         = LINUX
        -
        -ifneq ($(shell test -f /usr/include/sys/epoll.h && echo yes), yes)
        -default:
        -	@echo "epoll not found"
        -	@exit 1
        -endif
        -
        -EXTRA_OBJS  = $(TARGETDIR)/md.o
        -
        -CFLAGS      =
        -OTHER_FLAGS += -Wall -g -O0
        -DEFINES     = -D$(OS) -DDEBUG -DMD_HAVE_EPOLL -DMALLOC_STACK
        -
        -##########################
        -# Other possible defines:
        -# To use malloc(3) instead of mmap(2) for stack allocation:
        -# DEFINES += -DMALLOC_STACK
        -#
        -# To provision more than the default 16 thread-specific-data keys
        -# (but not too many!):
        -# DEFINES += -DST_KEYS_MAX=
        -#
        -# Note that you can also add these defines by specifying them as
        -# make/gmake arguments (without editing this Makefile). For example:
        -#
        -# make EXTRA_CFLAGS=-UMD_HAVE_EPOLL 
        -#
        -##########################
        -
        -CFLAGS      += $(DEFINES) $(OTHER_FLAGS) $(EXTRA_CFLAGS)
        -
        -OBJS        = $(TARGETDIR)/sched.o 	\
        -              $(TARGETDIR)/stk.o   	\
        -              $(TARGETDIR)/sync.o  	\
        -              $(TARGETDIR)/key.o   	\
        -              $(TARGETDIR)/io.o    	\
        -              $(TARGETDIR)/event.o	\
        -              $(TARGETDIR)/srs.o
        -OBJS        += $(EXTRA_OBJS)
        -SRS    		= $(TARGETDIR)/srs
        -
        -linux-debug: all
        -all: $(TARGETDIR) $(SRS)
        -
        -$(TARGETDIR):
        -	if [ ! -d $(TARGETDIR) ]; then mkdir $(TARGETDIR); fi
        -
        -$(SRS): $(OBJS)
        -	$(CC) $(CFLAGS) -o $@ $(OBJS)
        -
        -$(TARGETDIR)/md.o: md.S
        -	$(CC) $(CFLAGS) -c $< -o $@
        -
        -$(TARGETDIR)/%.o: %.c common.h md.h Makefile
        -	$(CC) $(CFLAGS) -c $< -o $@
        -
        -clean:
        -	rm -rf $(TARGETDIR)
        diff --git a/trunk/research/st/common.h b/trunk/research/st/common.h
        deleted file mode 100644
        index b83e575ca..000000000
        --- a/trunk/research/st/common.h
        +++ /dev/null
        @@ -1,445 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -/*
        - * This file is derived directly from Netscape Communications Corporation,
        - * and consists of extensive modifications made during the year(s) 1999-2000.
        - */
        -
        -#ifndef __ST_COMMON_H__
        -#define __ST_COMMON_H__
        -
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -
        -/* Enable assertions only if DEBUG is defined */
        -#ifndef DEBUG
        -    #define NDEBUG
        -#endif
        -#include 
        -#define ST_ASSERT(expr) assert(expr)
        -
        -#define ST_BEGIN_MACRO  {
        -#define ST_END_MACRO    }
        -
        -#ifdef DEBUG
        -    #define ST_HIDDEN   /*nothing*/
        -#else
        -    #define    ST_HIDDEN   static
        -#endif
        -
        -#include "public.h"
        -#include "md.h"
        -
        -/*****************************************
        - * Circular linked list definitions
        - */
        -
        -typedef struct _st_clist {
        -    struct _st_clist *next;
        -    struct _st_clist *prev;
        -} _st_clist_t;
        -
        -/* Insert element "_e" into the list, before "_l" */
        -#define ST_INSERT_BEFORE(_e,_l)     \
        -    ST_BEGIN_MACRO         \
        -    (_e)->next = (_l);     \
        -    (_e)->prev = (_l)->prev; \
        -    (_l)->prev->next = (_e); \
        -    (_l)->prev = (_e);     \
        -    ST_END_MACRO
        -
        -/* Insert element "_e" into the list, after "_l" */
        -#define ST_INSERT_AFTER(_e,_l)     \
        -    ST_BEGIN_MACRO         \
        -    (_e)->next = (_l)->next; \
        -    (_e)->prev = (_l);     \
        -    (_l)->next->prev = (_e); \
        -    (_l)->next = (_e);     \
        -    ST_END_MACRO
        -
        -/* Return the element following element "_e" */
        -#define ST_NEXT_LINK(_e)  ((_e)->next)
        -
        -/* Append an element "_e" to the end of the list "_l" */
        -#define ST_APPEND_LINK(_e,_l) ST_INSERT_BEFORE(_e,_l)
        -
        -/* Insert an element "_e" at the head of the list "_l" */
        -#define ST_INSERT_LINK(_e,_l) ST_INSERT_AFTER(_e,_l)
        -
        -/* Return the head/tail of the list */
        -#define ST_LIST_HEAD(_l) (_l)->next
        -#define ST_LIST_TAIL(_l) (_l)->prev
        -
        -/* Remove the element "_e" from it's circular list */
        -#define ST_REMOVE_LINK(_e)           \
        -    ST_BEGIN_MACRO               \
        -    (_e)->prev->next = (_e)->next; \
        -    (_e)->next->prev = (_e)->prev; \
        -    ST_END_MACRO
        -
        -/* Return non-zero if the given circular list "_l" is empty, */
        -/* zero if the circular list is not empty */
        -#define ST_CLIST_IS_EMPTY(_l) \
        -    ((_l)->next == (_l))
        -
        -/* Initialize a circular list */
        -#define ST_INIT_CLIST(_l)  \
        -    ST_BEGIN_MACRO       \
        -    (_l)->next = (_l); \
        -    (_l)->prev = (_l); \
        -    ST_END_MACRO
        -
        -#define ST_INIT_STATIC_CLIST(_l) \
        -    {(_l), (_l)}
        -
        -
        -/*****************************************
        - * Basic types definitions
        - */
        -
        -typedef void  (*_st_destructor_t)(void *);
        -
        -typedef struct _st_stack {
        -    _st_clist_t links;
        -    char *vaddr;                /* Base of stack's allocated memory */
        -    int  vaddr_size;            /* Size of stack's allocated memory */
        -    int  stk_size;              /* Size of usable portion of the stack */
        -    char *stk_bottom;           /* Lowest address of stack's usable portion */
        -    char *stk_top;              /* Highest address of stack's usable portion */
        -    void *sp;                   /* Stack pointer from C's point of view */
        -} _st_stack_t;
        -
        -
        -typedef struct _st_cond {
        -    _st_clist_t wait_q;          /* Condition variable wait queue */
        -} _st_cond_t;
        -
        -
        -typedef struct _st_thread _st_thread_t;
        -
        -struct _st_thread {
        -    int state;                  /* Thread's state */
        -    int flags;                  /* Thread's flags */
        -
        -    void *(*start)(void *arg);  /* The start function of the thread */
        -    void *arg;                  /* Argument of the start function */
        -    void *retval;               /* Return value of the start function */
        -
        -    _st_stack_t *stack;          /* Info about thread's stack */
        -    
        -    _st_clist_t links;          /* For putting on run/sleep/zombie queue */
        -    _st_clist_t wait_links;     /* For putting on mutex/condvar wait queue */
        -    #ifdef DEBUG
        -    _st_clist_t tlink;          /* For putting on thread queue */
        -    #endif
        -
        -    st_utime_t due;             /* Wakeup time when thread is sleeping */
        -    _st_thread_t *left;         /* For putting in timeout heap */
        -    _st_thread_t *right;          /* -- see docs/timeout_heap.txt for details */
        -    int heap_index;
        -    
        -    void **private_data;        /* Per thread private data */
        -    
        -    _st_cond_t *term;           /* Termination condition variable for join */
        -    
        -    jmp_buf context;            /* Thread's context */
        -};
        -
        -
        -typedef struct _st_mutex {
        -    _st_thread_t *owner;        /* Current mutex owner */
        -    _st_clist_t  wait_q;        /* Mutex wait queue */
        -} _st_mutex_t;
        -
        -
        -typedef struct _st_pollq {
        -    _st_clist_t links;          /* For putting on io queue */
        -    _st_thread_t  *thread;      /* Polling thread */
        -    struct pollfd *pds;         /* Array of poll descriptors */
        -    int npds;                   /* Length of the array */
        -    int on_ioq;                 /* Is it on ioq? */
        -} _st_pollq_t;
        -
        -
        -typedef struct _st_eventsys_ops {
        -    const char *name;                          /* Name of this event system */
        -    int  val;                                  /* Type of this event system */
        -    int  (*init)(void);                        /* Initialization */
        -    void (*dispatch)(void);                    /* Dispatch function */
        -    int  (*pollset_add)(struct pollfd *, int); /* Add descriptor set */
        -    void (*pollset_del)(struct pollfd *, int); /* Delete descriptor set */
        -    int  (*fd_new)(int);                       /* New descriptor allocated */
        -    int  (*fd_close)(int);                     /* Descriptor closed */
        -    int  (*fd_getlimit)(void);                 /* Descriptor hard limit */
        -} _st_eventsys_t;
        -
        -
        -typedef struct _st_vp {
        -    _st_thread_t *idle_thread;  /* Idle thread for this vp */
        -    st_utime_t last_clock;      /* The last time we went into vp_check_clock() */
        -    
        -    _st_clist_t run_q;          /* run queue for this vp */
        -    _st_clist_t io_q;           /* io queue for this vp */
        -    _st_clist_t zombie_q;       /* zombie queue for this vp */
        -    #ifdef DEBUG
        -    _st_clist_t thread_q;       /* all threads of this vp */
        -    #endif
        -    int pagesize;
        -    
        -    _st_thread_t *sleep_q;      /* sleep queue for this vp */
        -    int sleepq_size;          /* number of threads on sleep queue */
        -    
        -    #ifdef ST_SWITCH_CB
        -    st_switch_cb_t switch_out_cb;    /* called when a thread is switched out */
        -    st_switch_cb_t switch_in_cb;    /* called when a thread is switched in */
        -    #endif
        -} _st_vp_t;
        -
        -
        -typedef struct _st_netfd {
        -    int osfd;                   /* Underlying OS file descriptor */
        -    int inuse;                  /* In-use flag */
        -    void *private_data;         /* Per descriptor private data */
        -    _st_destructor_t destructor; /* Private data destructor function */
        -    void *aux_data;             /* Auxiliary data for internal use */
        -    struct _st_netfd *next;     /* For putting on the free list */
        -} _st_netfd_t;
        -
        -
        -/*****************************************
        - * Current vp, thread, and event system
        - */
        -
        -extern _st_vp_t        _st_this_vp;
        -extern _st_thread_t *_st_this_thread;
        -extern _st_eventsys_t *_st_eventsys;
        -
        -#define _ST_CURRENT_THREAD()            (_st_this_thread)
        -#define _ST_SET_CURRENT_THREAD(_thread) (_st_this_thread = (_thread))
        -
        -#define _ST_LAST_CLOCK                  (_st_this_vp.last_clock)
        -
        -#define _ST_RUNQ                        (_st_this_vp.run_q)
        -#define _ST_IOQ                         (_st_this_vp.io_q)
        -#define _ST_ZOMBIEQ                     (_st_this_vp.zombie_q)
        -#ifdef DEBUG
        -    #define _ST_THREADQ                     (_st_this_vp.thread_q)
        -#endif
        -
        -#define _ST_PAGE_SIZE                   (_st_this_vp.pagesize)
        -
        -#define _ST_SLEEPQ                      (_st_this_vp.sleep_q)
        -#define _ST_SLEEPQ_SIZE                 (_st_this_vp.sleepq_size)
        -
        -#define _ST_VP_IDLE()                   (*_st_eventsys->dispatch)()
        -
        -
        -/*****************************************
        - * vp queues operations
        - */
        -
        -#define _ST_ADD_IOQ(_pq)    ST_APPEND_LINK(&_pq.links, &_ST_IOQ)
        -#define _ST_DEL_IOQ(_pq)    ST_REMOVE_LINK(&_pq.links)
        -
        -#define _ST_ADD_RUNQ(_thr)  ST_APPEND_LINK(&(_thr)->links, &_ST_RUNQ)
        -#define _ST_DEL_RUNQ(_thr)  ST_REMOVE_LINK(&(_thr)->links)
        -
        -#define _ST_ADD_SLEEPQ(_thr, _timeout)  _st_add_sleep_q(_thr, _timeout)
        -#define _ST_DEL_SLEEPQ(_thr)        _st_del_sleep_q(_thr)
        -
        -#define _ST_ADD_ZOMBIEQ(_thr)  ST_APPEND_LINK(&(_thr)->links, &_ST_ZOMBIEQ)
        -#define _ST_DEL_ZOMBIEQ(_thr)  ST_REMOVE_LINK(&(_thr)->links)
        -
        -#ifdef DEBUG
        -    #define _ST_ADD_THREADQ(_thr)  ST_APPEND_LINK(&(_thr)->tlink, &_ST_THREADQ)
        -    #define _ST_DEL_THREADQ(_thr)  ST_REMOVE_LINK(&(_thr)->tlink)
        -#endif
        -
        -
        -/*****************************************
        - * Thread states and flags
        - */
        -
        -#define _ST_ST_RUNNING      0 
        -#define _ST_ST_RUNNABLE     1
        -#define _ST_ST_IO_WAIT      2
        -#define _ST_ST_LOCK_WAIT    3
        -#define _ST_ST_COND_WAIT    4
        -#define _ST_ST_SLEEPING     5
        -#define _ST_ST_ZOMBIE       6
        -#define _ST_ST_SUSPENDED    7
        -
        -#define _ST_FL_PRIMORDIAL   0x01
        -#define _ST_FL_IDLE_THREAD  0x02
        -#define _ST_FL_ON_SLEEPQ    0x04
        -#define _ST_FL_INTERRUPT    0x08
        -#define _ST_FL_TIMEDOUT     0x10
        -
        -/*****************************************
        - * Pointer conversion
        - */
        -
        -#ifndef offsetof
        -    #define offsetof(type, identifier) ((size_t)&(((type *)0)->identifier))
        -#endif
        -
        -#define _ST_THREAD_PTR(_qp)         \
        -    ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, links)))
        -
        -#define _ST_THREAD_WAITQ_PTR(_qp)   \
        -    ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, wait_links)))
        -
        -#define _ST_THREAD_STACK_PTR(_qp)   \
        -    ((_st_stack_t *)((char*)(_qp) - offsetof(_st_stack_t, links)))
        -
        -#define _ST_POLLQUEUE_PTR(_qp)      \
        -    ((_st_pollq_t *)((char *)(_qp) - offsetof(_st_pollq_t, links)))
        -
        -#ifdef DEBUG
        -    #define _ST_THREAD_THREADQ_PTR(_qp) \
        -        ((_st_thread_t *)((char *)(_qp) - offsetof(_st_thread_t, tlink)))
        -#endif
        -
        -
        -/*****************************************
        - * Constants
        - */
        -
        -#ifndef ST_UTIME_NO_TIMEOUT
        -    #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL)
        -#endif
        -
        -#define ST_DEFAULT_STACK_SIZE (64*1024)
        -
        -#ifndef ST_KEYS_MAX
        -    #define ST_KEYS_MAX 16
        -#endif
        -
        -#ifndef ST_MIN_POLLFDS_SIZE
        -    #define ST_MIN_POLLFDS_SIZE 64
        -#endif
        -
        -
        -/*****************************************
        - * Threads context switching
        - */
        -
        -#ifdef DEBUG
        -    void _st_iterate_threads(void);
        -    #define ST_DEBUG_ITERATE_THREADS() _st_iterate_threads()
        -#else
        -    #define ST_DEBUG_ITERATE_THREADS()
        -#endif
        -
        -#ifdef ST_SWITCH_CB
        -    #define ST_SWITCH_OUT_CB(_thread)        \
        -        if (_st_this_vp.switch_out_cb != NULL &&    \
        -            _thread != _st_this_vp.idle_thread &&    \
        -            _thread->state != _ST_ST_ZOMBIE) {    \
        -          _st_this_vp.switch_out_cb();        \
        -        }
        -    #define ST_SWITCH_IN_CB(_thread)        \
        -        if (_st_this_vp.switch_in_cb != NULL &&    \
        -        _thread != _st_this_vp.idle_thread &&    \
        -        _thread->state != _ST_ST_ZOMBIE) {    \
        -          _st_this_vp.switch_in_cb();        \
        -        }
        -#else
        -    #define ST_SWITCH_OUT_CB(_thread)
        -    #define ST_SWITCH_IN_CB(_thread)
        -#endif
        -
        -/*
        - * Switch away from the current thread context by saving its state and
        - * calling the thread scheduler
        - */
        -#define _ST_SWITCH_CONTEXT(_thread)       \
        -    ST_BEGIN_MACRO                        \
        -    ST_SWITCH_OUT_CB(_thread);            \
        -    if (!MD_SETJMP((_thread)->context)) { \
        -      _st_vp_schedule();                  \
        -    }                                     \
        -    ST_DEBUG_ITERATE_THREADS();           \
        -    ST_SWITCH_IN_CB(_thread);             \
        -    ST_END_MACRO
        -
        -/*
        - * Restore a thread context that was saved by _ST_SWITCH_CONTEXT or
        - * initialized by _ST_INIT_CONTEXT
        - */
        -#define _ST_RESTORE_CONTEXT(_thread)   \
        -    ST_BEGIN_MACRO                     \
        -    _ST_SET_CURRENT_THREAD(_thread);   \
        -    MD_LONGJMP((_thread)->context, 1); \
        -    ST_END_MACRO
        -
        -/*
        - * Number of bytes reserved under the stack "bottom"
        - */
        -#define _ST_STACK_PAD_SIZE MD_STACK_PAD_SIZE
        -
        -
        -/*****************************************
        - * Forward declarations
        - */
        -
        -void _st_vp_schedule(void);
        -void _st_vp_check_clock(void);
        -void *_st_idle_thread_start(void *arg);
        -void _st_thread_main(void);
        -void _st_thread_cleanup(_st_thread_t *thread);
        -void _st_add_sleep_q(_st_thread_t *thread, st_utime_t timeout);
        -void _st_del_sleep_q(_st_thread_t *thread);
        -_st_stack_t *_st_stack_new(int stack_size);
        -void _st_stack_free(_st_stack_t *ts);
        -int _st_io_init(void);
        -
        -st_utime_t st_utime(void);
        -_st_cond_t *st_cond_new(void);
        -int st_cond_destroy(_st_cond_t *cvar);
        -int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout);
        -int st_cond_signal(_st_cond_t *cvar);
        -ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout);
        -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout);
        -int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
        -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size);
        -
        -#endif /* !__ST_COMMON_H__ */
        -
        diff --git a/trunk/research/st/event.c b/trunk/research/st/event.c
        deleted file mode 100644
        index 5704f520a..000000000
        --- a/trunk/research/st/event.c
        +++ /dev/null
        @@ -1,483 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - *                  Yahoo! Inc.
        - *
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include "common.h"
        -
        -#ifdef USE_POLL
        -    #error "Not support USE_POLL"
        -#endif
        -#ifdef MD_HAVE_KQUEUE
        -    #error "Not support MD_HAVE_KQUEUE"
        -#endif
        -#ifdef MD_HAVE_POLL
        -    #error "Not support MD_HAVE_POLL"
        -#endif
        -#ifndef MD_HAVE_EPOLL
        -    #error "Only support MD_HAVE_EPOLL"
        -#endif
        -
        -#include 
        -
        -typedef struct _epoll_fd_data {
        -    int rd_ref_cnt;
        -    int wr_ref_cnt;
        -    int ex_ref_cnt;
        -    int revents;
        -} _epoll_fd_data_t;
        -
        -static struct _st_epolldata {
        -    _epoll_fd_data_t *fd_data;
        -    struct epoll_event *evtlist;
        -    int fd_data_size;
        -    int evtlist_size;
        -    int evtlist_cnt;
        -    int fd_hint;
        -    int epfd;
        -    pid_t pid;
        -} *_st_epoll_data;
        -
        -#ifndef ST_EPOLL_EVTLIST_SIZE
        -/* Not a limit, just a hint */
        -#define ST_EPOLL_EVTLIST_SIZE 4096
        -#endif
        -
        -#define _ST_EPOLL_READ_CNT(fd)   (_st_epoll_data->fd_data[fd].rd_ref_cnt)
        -#define _ST_EPOLL_WRITE_CNT(fd)  (_st_epoll_data->fd_data[fd].wr_ref_cnt)
        -#define _ST_EPOLL_EXCEP_CNT(fd)  (_st_epoll_data->fd_data[fd].ex_ref_cnt)
        -#define _ST_EPOLL_REVENTS(fd)    (_st_epoll_data->fd_data[fd].revents)
        -
        -#define _ST_EPOLL_READ_BIT(fd)   (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0)
        -#define _ST_EPOLL_WRITE_BIT(fd)  (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0)
        -#define _ST_EPOLL_EXCEP_BIT(fd)  (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0)
        -#define _ST_EPOLL_EVENTS(fd) \
        -    (_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd))
        -
        -_st_eventsys_t *_st_eventsys = NULL;
        -
        -/*****************************************
        - * epoll event system
        - */
        -
        -ST_HIDDEN int _st_epoll_init(void)
        -{
        -    int fdlim;
        -    int err = 0;
        -    int rv = 0;
        -
        -    _st_epoll_data = (struct _st_epolldata *) calloc(1, sizeof(*_st_epoll_data));
        -    if (!_st_epoll_data) {
        -        return -1;
        -    }
        -
        -    fdlim = st_getfdlimit();
        -    _st_epoll_data->fd_hint = (fdlim > 0 && fdlim < ST_EPOLL_EVTLIST_SIZE) ? fdlim : ST_EPOLL_EVTLIST_SIZE;
        -
        -    if ((_st_epoll_data->epfd = epoll_create(_st_epoll_data->fd_hint)) < 0) {
        -        err = errno;
        -        rv = -1;
        -        goto cleanup_epoll;
        -    }
        -    fcntl(_st_epoll_data->epfd, F_SETFD, FD_CLOEXEC);
        -    _st_epoll_data->pid = getpid();
        -
        -    /* Allocate file descriptor data array */
        -    _st_epoll_data->fd_data_size = _st_epoll_data->fd_hint;
        -    _st_epoll_data->fd_data = (_epoll_fd_data_t *)calloc(_st_epoll_data->fd_data_size, sizeof(_epoll_fd_data_t));
        -    if (!_st_epoll_data->fd_data) {
        -        err = errno;
        -        rv = -1;
        -        goto cleanup_epoll;
        -    }
        -
        -    /* Allocate event lists */
        -    _st_epoll_data->evtlist_size = _st_epoll_data->fd_hint;
        -    _st_epoll_data->evtlist = (struct epoll_event *)malloc(_st_epoll_data->evtlist_size * sizeof(struct epoll_event));
        -    if (!_st_epoll_data->evtlist) {
        -        err = errno;
        -        rv = -1;
        -    }
        -
        - cleanup_epoll:
        -    if (rv < 0) {
        -        if (_st_epoll_data->epfd >= 0) {
        -            close(_st_epoll_data->epfd);
        -        }
        -        free(_st_epoll_data->fd_data);
        -        free(_st_epoll_data->evtlist);
        -        free(_st_epoll_data);
        -        _st_epoll_data = NULL;
        -        errno = err;
        -    }
        -
        -    return rv;
        -}
        -
        -ST_HIDDEN int _st_epoll_fd_data_expand(int maxfd)
        -{
        -    _epoll_fd_data_t *ptr;
        -    int n = _st_epoll_data->fd_data_size;
        -
        -    while (maxfd >= n) {
        -        n <<= 1;
        -    }
        -
        -    ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, n * sizeof(_epoll_fd_data_t));
        -    if (!ptr) {
        -        return -1;
        -    }
        -
        -    memset(ptr + _st_epoll_data->fd_data_size, 0, (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t));
        -
        -    _st_epoll_data->fd_data = ptr;
        -    _st_epoll_data->fd_data_size = n;
        -
        -    return 0;
        -}
        -
        -ST_HIDDEN void _st_epoll_evtlist_expand(void)
        -{
        -    struct epoll_event *ptr;
        -    int n = _st_epoll_data->evtlist_size;
        -
        -    while (_st_epoll_data->evtlist_cnt > n) {
        -        n <<= 1;
        -    }
        -
        -    ptr = (struct epoll_event *)realloc(_st_epoll_data->evtlist, n * sizeof(struct epoll_event));
        -    if (ptr) {
        -        _st_epoll_data->evtlist = ptr;
        -        _st_epoll_data->evtlist_size = n;
        -    }
        -}
        -
        -ST_HIDDEN void _st_epoll_pollset_del(struct pollfd *pds, int npds)
        -{
        -    struct epoll_event ev;
        -    struct pollfd *pd;
        -    struct pollfd *epd = pds + npds;
        -    int old_events, events, op;
        -
        -    /*
        -     * It's more or less OK if deleting fails because a descriptor
        -     * will either be closed or deleted in dispatch function after
        -     * it fires.
        -     */
        -    for (pd = pds; pd < epd; pd++) {
        -        old_events = _ST_EPOLL_EVENTS(pd->fd);
        -
        -        if (pd->events & POLLIN) {
        -            _ST_EPOLL_READ_CNT(pd->fd)--;
        -        }
        -        if (pd->events & POLLOUT) {
        -            _ST_EPOLL_WRITE_CNT(pd->fd)--;
        -        }
        -        if (pd->events & POLLPRI) {
        -            _ST_EPOLL_EXCEP_CNT(pd->fd)--;
        -        }
        -
        -        events = _ST_EPOLL_EVENTS(pd->fd);
        -        /*
        -         * The _ST_EPOLL_REVENTS check below is needed so we can use
        -         * this function inside dispatch(). Outside of dispatch()
        -         * _ST_EPOLL_REVENTS is always zero for all descriptors.
        -         */
        -        if (events != old_events && _ST_EPOLL_REVENTS(pd->fd) == 0) {
        -            op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
        -            ev.events = events;
        -            ev.data.fd = pd->fd;
        -            if (epoll_ctl(_st_epoll_data->epfd, op, pd->fd, &ev) == 0 && op == EPOLL_CTL_DEL) {
        -                _st_epoll_data->evtlist_cnt--;
        -            }
        -        }
        -    }
        -}
        -
        -ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds)
        -{
        -    struct epoll_event ev;
        -    int i, fd;
        -    int old_events, events, op;
        -
        -    /* Do as many checks as possible up front */
        -    for (i = 0; i < npds; i++) {
        -        fd = pds[i].fd;
        -        if (fd < 0 || !pds[i].events || (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) {
        -            errno = EINVAL;
        -            return -1;
        -        }
        -        if (fd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(fd) < 0) {
        -            return -1;
        -        }
        -    }
        -
        -    for (i = 0; i < npds; i++) {
        -        fd = pds[i].fd;
        -        old_events = _ST_EPOLL_EVENTS(fd);
        -
        -        if (pds[i].events & POLLIN) {
        -            _ST_EPOLL_READ_CNT(fd)++;
        -        }
        -        if (pds[i].events & POLLOUT) {
        -            _ST_EPOLL_WRITE_CNT(fd)++;
        -        }
        -        if (pds[i].events & POLLPRI) {
        -            _ST_EPOLL_EXCEP_CNT(fd)++;
        -        }
        -
        -        events = _ST_EPOLL_EVENTS(fd);
        -        if (events != old_events) {
        -            op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
        -            ev.events = events;
        -            ev.data.fd = fd;
        -            if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 && (op != EPOLL_CTL_ADD || errno != EEXIST)) {
        -                break;
        -            }
        -            if (op == EPOLL_CTL_ADD) {
        -                _st_epoll_data->evtlist_cnt++;
        -                if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size) {
        -                    _st_epoll_evtlist_expand();
        -                }
        -            }
        -        }
        -    }
        -
        -    if (i < npds) {
        -        /* Error */
        -        int err = errno;
        -        /* Unroll the state */
        -        _st_epoll_pollset_del(pds, i + 1);
        -        errno = err;
        -        return -1;
        -    }
        -
        -    return 0;
        -}
        -
        -ST_HIDDEN void _st_epoll_dispatch(void)
        -{
        -    st_utime_t min_timeout;
        -    _st_clist_t *q;
        -    _st_pollq_t *pq;
        -    struct pollfd *pds, *epds;
        -    struct epoll_event ev;
        -    int timeout, nfd, i, osfd, notify;
        -    int events, op;
        -    short revents;
        -
        -    if (_ST_SLEEPQ == NULL) {
        -        timeout = -1;
        -    } else {
        -        min_timeout = (_ST_SLEEPQ->due <= _ST_LAST_CLOCK) ? 0 : (_ST_SLEEPQ->due - _ST_LAST_CLOCK);
        -        timeout = (int) (min_timeout / 1000);
        -    }
        -
        -    if (_st_epoll_data->pid != getpid()) {
        -        // WINLIN: remove it for bug introduced.
        -        // @see: https://github.com/ossrs/srs/issues/193
        -        exit(-1);
        -    }
        -
        -    /* Check for I/O operations */
        -    nfd = epoll_wait(_st_epoll_data->epfd, _st_epoll_data->evtlist, _st_epoll_data->evtlist_size, timeout);
        -
        -    if (nfd > 0) {
        -        for (i = 0; i < nfd; i++) {
        -            osfd = _st_epoll_data->evtlist[i].data.fd;
        -            _ST_EPOLL_REVENTS(osfd) = _st_epoll_data->evtlist[i].events;
        -            if (_ST_EPOLL_REVENTS(osfd) & (EPOLLERR | EPOLLHUP)) {
        -                /* Also set I/O bits on error */
        -                _ST_EPOLL_REVENTS(osfd) |= _ST_EPOLL_EVENTS(osfd);
        -            }
        -        }
        -
        -        for (q = _ST_IOQ.next; q != &_ST_IOQ; q = q->next) {
        -            pq = _ST_POLLQUEUE_PTR(q);
        -            notify = 0;
        -            epds = pq->pds + pq->npds;
        -
        -            for (pds = pq->pds; pds < epds; pds++) {
        -                if (_ST_EPOLL_REVENTS(pds->fd) == 0) {
        -                    pds->revents = 0;
        -                    continue;
        -                }
        -                osfd = pds->fd;
        -                events = pds->events;
        -                revents = 0;
        -                if ((events & POLLIN) && (_ST_EPOLL_REVENTS(osfd) & EPOLLIN)) {
        -                    revents |= POLLIN;
        -                }
        -                if ((events & POLLOUT) && (_ST_EPOLL_REVENTS(osfd) & EPOLLOUT)) {
        -                    revents |= POLLOUT;
        -                }
        -                if ((events & POLLPRI) && (_ST_EPOLL_REVENTS(osfd) & EPOLLPRI)) {
        -                    revents |= POLLPRI;
        -                }
        -                if (_ST_EPOLL_REVENTS(osfd) & EPOLLERR) {
        -                    revents |= POLLERR;
        -                }
        -                if (_ST_EPOLL_REVENTS(osfd) & EPOLLHUP) {
        -                    revents |= POLLHUP;
        -                }
        -
        -                pds->revents = revents;
        -                if (revents) {
        -                    notify = 1;
        -                }
        -            }
        -            if (notify) {
        -                ST_REMOVE_LINK(&pq->links);
        -                pq->on_ioq = 0;
        -                /*
        -                 * Here we will only delete/modify descriptors that
        -                 * didn't fire (see comments in _st_epoll_pollset_del()).
        -                 */
        -                _st_epoll_pollset_del(pq->pds, pq->npds);
        -
        -                if (pq->thread->flags & _ST_FL_ON_SLEEPQ) {
        -                    _ST_DEL_SLEEPQ(pq->thread);
        -                }
        -                pq->thread->state = _ST_ST_RUNNABLE;
        -                _ST_ADD_RUNQ(pq->thread);
        -            }
        -        }
        -
        -        for (i = 0; i < nfd; i++) {
        -            /* Delete/modify descriptors that fired */
        -            osfd = _st_epoll_data->evtlist[i].data.fd;
        -            _ST_EPOLL_REVENTS(osfd) = 0;
        -            events = _ST_EPOLL_EVENTS(osfd);
        -            op = events ? EPOLL_CTL_MOD : EPOLL_CTL_DEL;
        -            ev.events = events;
        -            ev.data.fd = osfd;
        -            if (epoll_ctl(_st_epoll_data->epfd, op, osfd, &ev) == 0 && op == EPOLL_CTL_DEL) {
        -                _st_epoll_data->evtlist_cnt--;
        -            }
        -        }
        -    }
        -}
        -
        -ST_HIDDEN int _st_epoll_fd_new(int osfd)
        -{
        -    if (osfd >= _st_epoll_data->fd_data_size && _st_epoll_fd_data_expand(osfd) < 0) {
        -        return -1;
        -    }
        -
        -    return 0;   
        -}
        -
        -ST_HIDDEN int _st_epoll_fd_close(int osfd)
        -{
        -    if (_ST_EPOLL_READ_CNT(osfd) || _ST_EPOLL_WRITE_CNT(osfd) || _ST_EPOLL_EXCEP_CNT(osfd)) {
        -        errno = EBUSY;
        -        return -1;
        -    }
        -
        -    return 0;
        -}
        -
        -ST_HIDDEN int _st_epoll_fd_getlimit(void)
        -{
        -    /* zero means no specific limit */
        -    return 0;
        -}
        -
        -/*
        - * Check if epoll functions are just stubs.
        - */
        -ST_HIDDEN int _st_epoll_is_supported(void)
        -{
        -    struct epoll_event ev;
        -
        -    ev.events = EPOLLIN;
        -    ev.data.ptr = NULL;
        -    /* Guaranteed to fail */
        -    epoll_ctl(-1, EPOLL_CTL_ADD, -1, &ev);
        -
        -    return (errno != ENOSYS);
        -}
        -
        -static _st_eventsys_t _st_epoll_eventsys = {
        -    "epoll",
        -    ST_EVENTSYS_ALT,
        -    _st_epoll_init,
        -    _st_epoll_dispatch,
        -    _st_epoll_pollset_add,
        -    _st_epoll_pollset_del,
        -    _st_epoll_fd_new,
        -    _st_epoll_fd_close,
        -    _st_epoll_fd_getlimit
        -};
        -
        -/*****************************************
        - * Public functions
        - */
        -
        -int st_set_eventsys(int eventsys)
        -{
        -    if (_st_eventsys) {
        -        errno = EBUSY;
        -        return -1;
        -    }
        -
        -    switch (eventsys) {
        -    case ST_EVENTSYS_DEFAULT:
        -    case ST_EVENTSYS_ALT:
        -    default:
        -        if (_st_epoll_is_supported()) {
        -            _st_eventsys = &_st_epoll_eventsys;
        -            break;
        -        }
        -        errno = EINVAL;
        -        return -1;
        -    }
        -
        -    return 0;
        -}
        -
        -int st_get_eventsys(void)
        -{
        -    return _st_eventsys ? _st_eventsys->val : -1;
        -}
        -
        -const char *st_get_eventsys_name(void)
        -{
        -    return _st_eventsys ? _st_eventsys->name : "";
        -}
        -
        diff --git a/trunk/research/st/io.c b/trunk/research/st/io.c
        deleted file mode 100644
        index bc77dc8e1..000000000
        --- a/trunk/research/st/io.c
        +++ /dev/null
        @@ -1,792 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -/*
        - * This file is derived directly from Netscape Communications Corporation,
        - * and consists of extensive modifications made during the year(s) 1999-2000.
        - */
        -
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include "common.h"
        -
        -#if EAGAIN != EWOULDBLOCK
        -    #define _IO_NOT_READY_ERROR  ((errno == EAGAIN) || (errno == EWOULDBLOCK))
        -#else
        -    #define _IO_NOT_READY_ERROR  (errno == EAGAIN)
        -#endif
        -
        -#define _LOCAL_MAXIOV  16
        -
        -/* File descriptor object free list */
        -static _st_netfd_t *_st_netfd_freelist = NULL;
        -/* Maximum number of file descriptors that the process can open */
        -static int _st_osfd_limit = -1;
        -
        -static void _st_netfd_free_aux_data(_st_netfd_t *fd);
        -
        -int _st_io_init(void)
        -{
        -    struct sigaction sigact;
        -    struct rlimit rlim;
        -    int fdlim;
        -
        -    /* Ignore SIGPIPE */
        -    sigact.sa_handler = SIG_IGN;
        -    sigemptyset(&sigact.sa_mask);
        -    sigact.sa_flags = 0;
        -    if (sigaction(SIGPIPE, &sigact, NULL) < 0) {
        -        return -1;
        -    }
        -
        -    /* Set maximum number of open file descriptors */
        -    if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
        -        return -1;
        -    }
        -
        -    fdlim = (*_st_eventsys->fd_getlimit)();
        -    if (fdlim > 0 && rlim.rlim_max > (rlim_t) fdlim) {
        -        rlim.rlim_max = fdlim;
        -    }
        -    rlim.rlim_cur = rlim.rlim_max;
        -    if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
        -        return -1;
        -    }
        -    _st_osfd_limit = (int) rlim.rlim_max;
        -    
        -    return 0;
        -}
        -
        -int st_getfdlimit(void)
        -{
        -    return _st_osfd_limit;
        -}
        -
        -void st_netfd_free(_st_netfd_t *fd)
        -{
        -    if (!fd->inuse) {
        -        return;
        -    }
        -    
        -    fd->inuse = 0;
        -    if (fd->aux_data) {
        -        _st_netfd_free_aux_data(fd);
        -    }
        -    if (fd->private_data && fd->destructor) {
        -        (*(fd->destructor))(fd->private_data);
        -    }
        -    fd->private_data = NULL;
        -    fd->destructor = NULL;
        -    fd->next = _st_netfd_freelist;
        -    _st_netfd_freelist = fd;
        -}
        -
        -static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket)
        -{
        -    _st_netfd_t *fd;
        -    int flags = 1;
        -    
        -    if ((*_st_eventsys->fd_new)(osfd) < 0) {
        -        return NULL;
        -    }
        -    
        -    if (_st_netfd_freelist) {
        -        fd = _st_netfd_freelist;
        -        _st_netfd_freelist = _st_netfd_freelist->next;
        -    } else {
        -        fd = calloc(1, sizeof(_st_netfd_t));
        -        if (!fd) {
        -            return NULL;
        -        }
        -    }
        -    
        -    fd->osfd = osfd;
        -    fd->inuse = 1;
        -    fd->next = NULL;
        -    
        -    if (nonblock) {
        -        /* Use just one system call */
        -        if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) {
        -            return fd;
        -        }
        -        /* Do it the Posix way */
        -        if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) {
        -            st_netfd_free(fd);
        -            return NULL;
        -        }
        -    }
        -    
        -    return fd;
        -}
        -
        -_st_netfd_t *st_netfd_open(int osfd)
        -{
        -    return _st_netfd_new(osfd, 1, 0);
        -}
        -
        -_st_netfd_t *st_netfd_open_socket(int osfd)
        -{
        -    return _st_netfd_new(osfd, 1, 1);
        -}
        -
        -int st_netfd_close(_st_netfd_t *fd)
        -{
        -    if ((*_st_eventsys->fd_close)(fd->osfd) < 0) {
        -        return -1;
        -    }
        -
        -    st_netfd_free(fd);
        -    return close(fd->osfd);
        -}
        -
        -int st_netfd_fileno(_st_netfd_t *fd)
        -{
        -    return (fd->osfd);
        -}
        -
        -void st_netfd_setspecific(_st_netfd_t *fd, void *value, _st_destructor_t destructor)
        -{
        -    if (value != fd->private_data) {
        -        /* Free up previously set non-NULL data value */
        -        if (fd->private_data && fd->destructor) {
        -            (*(fd->destructor))(fd->private_data);
        -        }
        -    }
        -    fd->private_data = value;
        -    fd->destructor = destructor;
        -}
        -
        -void *st_netfd_getspecific(_st_netfd_t *fd)
        -{
        -    return (fd->private_data);
        -}
        -
        -/*
        - * Wait for I/O on a single descriptor.
        - */
        -int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeout)
        -{
        -    struct pollfd pd;
        -    int n;
        -    
        -    pd.fd = fd->osfd;
        -    pd.events = (short) how;
        -    pd.revents = 0;
        -    
        -    if ((n = st_poll(&pd, 1, timeout)) < 0) {
        -        return -1;
        -    }
        -    if (n == 0) {
        -        /* Timed out */
        -        errno = ETIME;
        -        return -1;
        -    }
        -    if (pd.revents & POLLNVAL) {
        -        errno = EBADF;
        -        return -1;
        -    }
        -    
        -    return 0;
        -}
        -
        -#ifdef MD_ALWAYS_UNSERIALIZED_ACCEPT
        -/* No-op */
        -int st_netfd_serialize_accept(_st_netfd_t *fd)
        -{
        -    fd->aux_data = NULL;
        -    return 0;
        -}
        -
        -/* No-op */
        -static void _st_netfd_free_aux_data(_st_netfd_t *fd)
        -{
        -    fd->aux_data = NULL;
        -}
        -
        -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout)
        -{
        -    int osfd, err;
        -    _st_netfd_t *newfd;
        -    
        -    while ((osfd = accept(fd->osfd, addr, (socklen_t *)addrlen)) < 0) {
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return NULL;
        -        }
        -        /* Wait until the socket becomes readable */
        -        if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
        -            return NULL;
        -        }
        -    }
        -    
        -    /* On some platforms the new socket created by accept() inherits */
        -    /* the nonblocking attribute of the listening socket */
        -#if defined (MD_ACCEPT_NB_INHERITED)
        -    newfd = _st_netfd_new(osfd, 0, 1);
        -#elif defined (MD_ACCEPT_NB_NOT_INHERITED)
        -    newfd = _st_netfd_new(osfd, 1, 1);
        -#else
        -    #error Unknown OS
        -#endif
        -    
        -    if (!newfd) {
        -        err = errno;
        -        close(osfd);
        -        errno = err;
        -    }
        -    
        -    return newfd;
        -}
        -
        -#else /* MD_ALWAYS_UNSERIALIZED_ACCEPT */
        -/*
        - * On some platforms accept() calls from different processes
        - * on the same listen socket must be serialized.
        - * The following code serializes accept()'s without process blocking.
        - * A pipe is used as an inter-process semaphore.
        - */
        -int st_netfd_serialize_accept(_st_netfd_t *fd)
        -{
        -    _st_netfd_t **p;
        -    int osfd[2], err;
        -    
        -    if (fd->aux_data) {
        -        errno = EINVAL;
        -        return -1;
        -    }
        -    if ((p = (_st_netfd_t **)calloc(2, sizeof(_st_netfd_t *))) == NULL) {
        -        return -1;
        -    }
        -    if (pipe(osfd) < 0) {
        -        free(p);
        -        return -1;
        -    }
        -    if ((p[0] = st_netfd_open(osfd[0])) != NULL && (p[1] = st_netfd_open(osfd[1])) != NULL && write(osfd[1], " ", 1) == 1) {
        -        fd->aux_data = p;
        -        return 0;
        -    }
        -    /* Error */
        -    err = errno;
        -    if (p[0]) {
        -        st_netfd_free(p[0]);
        -    }
        -    if (p[1]) {
        -        st_netfd_free(p[1]);
        -    }
        -    close(osfd[0]);
        -    close(osfd[1]);
        -    free(p);
        -    errno = err;
        -    
        -    return -1;
        -}
        -
        -static void _st_netfd_free_aux_data(_st_netfd_t *fd)
        -{
        -    _st_netfd_t **p = (_st_netfd_t **) fd->aux_data;
        -    
        -    st_netfd_close(p[0]);
        -    st_netfd_close(p[1]);
        -    free(p);
        -    fd->aux_data = NULL;
        -}
        -
        -_st_netfd_t *st_accept(_st_netfd_t *fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout)
        -{
        -    int osfd, err;
        -    _st_netfd_t *newfd;
        -    _st_netfd_t **p = (_st_netfd_t **) fd->aux_data;
        -    ssize_t n;
        -    char c;
        -    
        -    for ( ; ; ) {
        -        if (p == NULL) {
        -            osfd = accept(fd->osfd, addr, (socklen_t *)addrlen);
        -        } else {
        -            /* Get the lock */
        -            n = st_read(p[0], &c, 1, timeout);
        -            if (n < 0) {
        -                return NULL;
        -            }
        -            ST_ASSERT(n == 1);
        -            /* Got the lock */
        -            osfd = accept(fd->osfd, addr, (socklen_t *)addrlen);
        -            /* Unlock */
        -            err = errno;
        -            n = st_write(p[1], &c, 1, timeout);
        -            ST_ASSERT(n == 1);
        -            errno = err;
        -        }
        -        if (osfd >= 0) {
        -            break;
        -        }
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return NULL;
        -        }
        -        /* Wait until the socket becomes readable */
        -        if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
        -            return NULL;
        -        }
        -    }
        -    
        -    /* On some platforms the new socket created by accept() inherits */
        -    /* the nonblocking attribute of the listening socket */
        -#if defined (MD_ACCEPT_NB_INHERITED)
        -    newfd = _st_netfd_new(osfd, 0, 1);
        -#elif defined (MD_ACCEPT_NB_NOT_INHERITED)
        -    newfd = _st_netfd_new(osfd, 1, 1);
        -#else
        -    #error Unknown OS
        -#endif
        -    
        -    if (!newfd) {
        -        err = errno;
        -        close(osfd);
        -        errno = err;
        -    }
        -    
        -    return newfd;
        -}
        -#endif /* MD_ALWAYS_UNSERIALIZED_ACCEPT */
        -
        -int st_connect(_st_netfd_t *fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout)
        -{
        -    int n, err = 0;
        -    
        -    while (connect(fd->osfd, addr, addrlen) < 0) {
        -        if (errno != EINTR) {
        -            /*
        -            * On some platforms, if connect() is interrupted (errno == EINTR)
        -            * after the kernel binds the socket, a subsequent connect()
        -            * attempt will fail with errno == EADDRINUSE.  Ignore EADDRINUSE
        -            * iff connect() was previously interrupted.  See Rich Stevens'
        -            * "UNIX Network Programming," Vol. 1, 2nd edition, p. 413
        -            * ("Interrupted connect").
        -            */
        -            if (errno != EINPROGRESS && (errno != EADDRINUSE || err == 0)) {
        -                return -1;
        -            }
        -            /* Wait until the socket becomes writable */
        -            if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
        -                return -1;
        -            }
        -            /* Try to find out whether the connection setup succeeded or failed */
        -            n = sizeof(int);
        -            if (getsockopt(fd->osfd, SOL_SOCKET, SO_ERROR, (char *)&err, (socklen_t *)&n) < 0) {
        -                return -1;
        -            }
        -            if (err) {
        -                errno = err;
        -                return -1;
        -            }
        -            break;
        -        }
        -        err = 1;
        -    }
        -    
        -    return 0;
        -}
        -
        -ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout)
        -{
        -    ssize_t n;
        -    
        -    while ((n = read(fd->osfd, buf, nbyte)) < 0) {
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return -1;
        -        }
        -        /* Wait until the socket becomes readable */
        -        if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return n;
        -}
        -
        -int st_read_resid(_st_netfd_t *fd, void *buf, size_t *resid, st_utime_t timeout)
        -{
        -    struct iovec iov, *riov;
        -    int riov_size, rv;
        -    
        -    iov.iov_base = buf;
        -    iov.iov_len = *resid;
        -    riov = &iov;
        -    riov_size = 1;
        -    rv = st_readv_resid(fd, &riov, &riov_size, timeout);
        -    *resid = iov.iov_len;
        -    return rv;
        -}
        -
        -ssize_t st_readv(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout)
        -{
        -    ssize_t n;
        -    
        -    while ((n = readv(fd->osfd, iov, iov_size)) < 0) {
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return -1;
        -        }
        -        /* Wait until the socket becomes readable */
        -        if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return n;
        -}
        -
        -int st_readv_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout)
        -{
        -    ssize_t n;
        -    
        -    while (*iov_size > 0) {
        -        if (*iov_size == 1) {
        -            n = read(fd->osfd, (*iov)->iov_base, (*iov)->iov_len);
        -        } else {
        -            n = readv(fd->osfd, *iov, *iov_size);
        -        }
        -        if (n < 0) {
        -            if (errno == EINTR) {
        -                continue;
        -            }
        -            if (!_IO_NOT_READY_ERROR) {
        -                return -1;
        -            }
        -        } else if (n == 0) {
        -            break;
        -        } else {
        -            while ((size_t) n >= (*iov)->iov_len) {
        -                n -= (*iov)->iov_len;
        -                (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len;
        -                (*iov)->iov_len = 0;
        -                (*iov)++;
        -                (*iov_size)--;
        -                if (n == 0) {
        -                    break;
        -                }
        -            }
        -            if (*iov_size == 0) {
        -                break;
        -            }
        -            (*iov)->iov_base = (char *) (*iov)->iov_base + n;
        -            (*iov)->iov_len -= n;
        -        }
        -        /* Wait until the socket becomes readable */
        -        if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return 0;
        -}
        -
        -ssize_t st_read_fully(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout)
        -{
        -    size_t resid = nbyte;
        -    return st_read_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1;
        -}
        -
        -int st_write_resid(_st_netfd_t *fd, const void *buf, size_t *resid, st_utime_t timeout)
        -{
        -    struct iovec iov, *riov;
        -    int riov_size, rv;
        -    
        -    iov.iov_base = (void *) buf;        /* we promise not to modify buf */
        -    iov.iov_len = *resid;
        -    riov = &iov;
        -    riov_size = 1;
        -    rv = st_writev_resid(fd, &riov, &riov_size, timeout);
        -    *resid = iov.iov_len;
        -    return rv;
        -}
        -
        -ssize_t st_write(_st_netfd_t *fd, const void *buf, size_t nbyte, st_utime_t timeout)
        -{
        -    size_t resid = nbyte;
        -    return st_write_resid(fd, buf, &resid, timeout) == 0 ? (ssize_t) (nbyte - resid) : -1;
        -}
        -
        -ssize_t st_writev(_st_netfd_t *fd, const struct iovec *iov, int iov_size, st_utime_t timeout)
        -{
        -    ssize_t n, rv;
        -    size_t nleft, nbyte;
        -    int index, iov_cnt;
        -    struct iovec *tmp_iov;
        -    struct iovec local_iov[_LOCAL_MAXIOV];
        -    
        -    /* Calculate the total number of bytes to be sent */
        -    nbyte = 0;
        -    for (index = 0; index < iov_size; index++) {
        -        nbyte += iov[index].iov_len;
        -    }
        -    
        -    rv = (ssize_t)nbyte;
        -    nleft = nbyte;
        -    tmp_iov = (struct iovec *) iov;    /* we promise not to modify iov */
        -    iov_cnt = iov_size;
        -    
        -    while (nleft > 0) {
        -        if (iov_cnt == 1) {
        -            if (st_write(fd, tmp_iov[0].iov_base, nleft, timeout) != (ssize_t) nleft) {
        -                rv = -1;
        -            }
        -            break;
        -        }
        -        if ((n = writev(fd->osfd, tmp_iov, iov_cnt)) < 0) {
        -            if (errno == EINTR) {
        -                continue;
        -            }
        -            if (!_IO_NOT_READY_ERROR) {
        -                rv = -1;
        -                break;
        -            }
        -        } else {
        -            if ((size_t) n == nleft) {
        -                break;
        -            }
        -            nleft -= n;
        -            /* Find the next unwritten vector */
        -            n = (ssize_t)(nbyte - nleft);
        -            for (index = 0; (size_t) n >= iov[index].iov_len; index++) {
        -                n -= iov[index].iov_len;
        -            }
        -            
        -            if (tmp_iov == iov) {
        -                /* Must copy iov's around */
        -                if (iov_size - index <= _LOCAL_MAXIOV) {
        -                    tmp_iov = local_iov;
        -                } else {
        -                    tmp_iov = calloc(1, (iov_size - index) * sizeof(struct iovec));
        -                    if (tmp_iov == NULL) {
        -                        return -1;
        -                    }
        -                }
        -            }
        -            
        -            /* Fill in the first partial read */
        -            tmp_iov[0].iov_base = &(((char *)iov[index].iov_base)[n]);
        -            tmp_iov[0].iov_len = iov[index].iov_len - n;
        -            index++;
        -            /* Copy the remaining vectors */
        -            for (iov_cnt = 1; index < iov_size; iov_cnt++, index++) {
        -                tmp_iov[iov_cnt].iov_base = iov[index].iov_base;
        -                tmp_iov[iov_cnt].iov_len = iov[index].iov_len;
        -            }
        -        }
        -        /* Wait until the socket becomes writable */
        -        if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
        -            rv = -1;
        -            break;
        -        }
        -    }
        -    
        -    if (tmp_iov != iov && tmp_iov != local_iov) {
        -        free(tmp_iov);
        -    }
        -    
        -    return rv;
        -}
        -
        -int st_writev_resid(_st_netfd_t *fd, struct iovec **iov, int *iov_size, st_utime_t timeout)
        -{
        -    ssize_t n;
        -    
        -    while (*iov_size > 0) {
        -        if (*iov_size == 1) {
        -            n = write(fd->osfd, (*iov)->iov_base, (*iov)->iov_len);
        -        } else {
        -            n = writev(fd->osfd, *iov, *iov_size);
        -        }
        -        if (n < 0) {
        -            if (errno == EINTR) {
        -                continue;
        -            }
        -            if (!_IO_NOT_READY_ERROR) {
        -                return -1;
        -            }
        -        } else {
        -            while ((size_t) n >= (*iov)->iov_len) {
        -                n -= (*iov)->iov_len;
        -                (*iov)->iov_base = (char *) (*iov)->iov_base + (*iov)->iov_len;
        -                (*iov)->iov_len = 0;
        -                (*iov)++;
        -                (*iov_size)--;
        -                if (n == 0) {
        -                    break;
        -                }
        -            }
        -            if (*iov_size == 0) {
        -                break;
        -            }
        -            (*iov)->iov_base = (char *) (*iov)->iov_base + n;
        -            (*iov)->iov_len -= n;
        -        }
        -        /* Wait until the socket becomes writable */
        -        if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return 0;
        -}
        -
        -/*
        - * Simple I/O functions for UDP.
        - */
        -int st_recvfrom(_st_netfd_t *fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout)
        -{
        -    int n;
        -    
        -    while ((n = recvfrom(fd->osfd, buf, len, 0, from, (socklen_t *)fromlen)) < 0) {
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return -1;
        -        }
        -        /* Wait until the socket becomes readable */
        -        if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return n;
        -}
        -
        -int st_sendto(_st_netfd_t *fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout)
        -{
        -    int n;
        -    
        -    while ((n = sendto(fd->osfd, msg, len, 0, to, tolen)) < 0) {
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return -1;
        -        }
        -        /* Wait until the socket becomes writable */
        -        if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return n;
        -}
        -
        -int st_recvmsg(_st_netfd_t *fd, struct msghdr *msg, int flags, st_utime_t timeout)
        -{
        -    int n;
        -    
        -    while ((n = recvmsg(fd->osfd, msg, flags)) < 0) {
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return -1;
        -        }
        -        /* Wait until the socket becomes readable */
        -        if (st_netfd_poll(fd, POLLIN, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return n;
        -}
        -
        -int st_sendmsg(_st_netfd_t *fd, const struct msghdr *msg, int flags, st_utime_t timeout)
        -{
        -    int n;
        -    
        -    while ((n = sendmsg(fd->osfd, msg, flags)) < 0) {
        -        if (errno == EINTR) {
        -            continue;
        -        }
        -        if (!_IO_NOT_READY_ERROR) {
        -            return -1;
        -        }
        -        /* Wait until the socket becomes writable */
        -        if (st_netfd_poll(fd, POLLOUT, timeout) < 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    return n;
        -}
        -
        -/*
        - * To open FIFOs or other special files.
        - */
        -_st_netfd_t *st_open(const char *path, int oflags, mode_t mode)
        -{
        -    int osfd, err;
        -    _st_netfd_t *newfd;
        -    
        -    while ((osfd = open(path, oflags | O_NONBLOCK, mode)) < 0) {
        -        if (errno != EINTR) {
        -            return NULL;
        -        }
        -    }
        -    
        -    newfd = _st_netfd_new(osfd, 0, 0);
        -    if (!newfd) {
        -        err = errno;
        -        close(osfd);
        -        errno = err;
        -    }
        -    
        -    return newfd;
        -}
        -
        diff --git a/trunk/research/st/key.c b/trunk/research/st/key.c
        deleted file mode 100644
        index 49d778a18..000000000
        --- a/trunk/research/st/key.c
        +++ /dev/null
        @@ -1,116 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -/*
        - * This file is derived directly from Netscape Communications Corporation,
        - * and consists of extensive modifications made during the year(s) 1999-2000.
        - */
        -
        -#include 
        -#include 
        -#include "common.h"
        -
        -/*
        - * Destructor table for per-thread private data
        - */
        -static _st_destructor_t _st_destructors[ST_KEYS_MAX];
        -static int key_max = 0;
        -
        -/*
        - * Return a key to be used for thread specific data
        - */
        -int st_key_create(int *keyp, _st_destructor_t destructor)
        -{
        -    if (key_max >= ST_KEYS_MAX) {
        -        errno = EAGAIN;
        -        return -1;
        -    }
        -    
        -    *keyp = key_max++;
        -    _st_destructors[*keyp] = destructor;
        -    
        -    return 0;
        -}
        -
        -int st_key_getlimit(void)
        -{
        -    return ST_KEYS_MAX;
        -}
        -
        -int st_thread_setspecific(int key, void *value)
        -{
        -    _st_thread_t *me = _ST_CURRENT_THREAD();
        -    
        -    if (key < 0 || key >= key_max) {
        -        errno = EINVAL;
        -        return -1;
        -    }
        -    
        -    if (value != me->private_data[key]) {
        -        /* free up previously set non-NULL data value */
        -        if (me->private_data[key] && _st_destructors[key]) {
        -            (*_st_destructors[key])(me->private_data[key]);
        -        }
        -        me->private_data[key] = value;
        -    }
        -    
        -    return 0;
        -}
        -
        -void *st_thread_getspecific(int key)
        -{
        -    if (key < 0 || key >= key_max) {
        -        return NULL;
        -    }
        -    
        -    return ((_ST_CURRENT_THREAD())->private_data[key]);
        -}
        -
        -/*
        - * Free up all per-thread private data
        - */
        -void _st_thread_cleanup(_st_thread_t *thread)
        -{
        -    int key;
        -    
        -    for (key = 0; key < key_max; key++) {
        -        if (thread->private_data[key] && _st_destructors[key]) {
        -            (*_st_destructors[key])(thread->private_data[key]);
        -            thread->private_data[key] = NULL;
        -        }
        -    }
        -}
        -
        diff --git a/trunk/research/st/md.S b/trunk/research/st/md.S
        deleted file mode 100755
        index 883da302b..000000000
        --- a/trunk/research/st/md.S
        +++ /dev/null
        @@ -1,151 +0,0 @@
        -/*
        - * Portions created by SGI are Copyright (C) 2000 Silicon Graphics, Inc.
        - * All Rights Reserved.
        - */
        -
        -/****************************************************************/
        -
        -#if defined(__i386__)
        -
        -/*
        - * Internal __jmp_buf layout
        - */
        -#define JB_BX  0
        -#define JB_SI  1
        -#define JB_DI  2
        -#define JB_BP  3
        -#define JB_SP  4
        -#define JB_PC  5
        -
        -        .file "md.S"
        -        .text
        -
        -        /* _st_md_cxt_save(__jmp_buf env) */
        -.globl _st_md_cxt_save
        -        .type _st_md_cxt_save, @function
        -        .align 16
        -_st_md_cxt_save:
        -        movl 4(%esp), %eax
        -
        -        /*
        -         * Save registers.
        -         */
        -        movl %ebx, (JB_BX*4)(%eax)
        -        movl %esi, (JB_SI*4)(%eax)
        -        movl %edi, (JB_DI*4)(%eax)
        -        /* Save SP */
        -        leal 4(%esp), %ecx
        -        movl %ecx, (JB_SP*4)(%eax)
        -        /* Save PC we are returning to */
        -        movl 0(%esp), %ecx
        -        movl %ecx, (JB_PC*4)(%eax)
        -        /* Save caller frame pointer */
        -        movl %ebp, (JB_BP*4)(%eax)
        -        xorl %eax, %eax
        -        ret
        -        .size _st_md_cxt_save, .-_st_md_cxt_save
        -
        -
        -/****************************************************************/
        -
        -        /* _st_md_cxt_restore(__jmp_buf env, int val) */
        -.globl _st_md_cxt_restore
        -        .type _st_md_cxt_restore, @function
        -        .align 16
        -_st_md_cxt_restore:
        -        /* First argument is jmp_buf */
        -        movl 4(%esp), %ecx
        -        /* Second argument is return value */
        -        movl 8(%esp), %eax
        -        /* Set the return address */
        -        movl (JB_PC*4)(%ecx), %edx
        -        /*
        -         * Restore registers.
        -         */
        -        movl (JB_BX*4)(%ecx), %ebx
        -        movl (JB_SI*4)(%ecx), %esi
        -        movl (JB_DI*4)(%ecx), %edi
        -        movl (JB_BP*4)(%ecx), %ebp
        -        movl (JB_SP*4)(%ecx), %esp
        -        testl %eax, %eax
        -        jnz  1f
        -        incl %eax
        -        /* Jump to saved PC */
        -1:      jmp *%edx
        -        .size _st_md_cxt_restore, .-_st_md_cxt_restore
        -
        -/****************************************************************/
        -
        -#elif defined(__amd64__) || defined(__x86_64__)
        -
        -/*
        - * Internal __jmp_buf layout
        - */
        -#define JB_RBX  0
        -#define JB_RBP  1
        -#define JB_R12  2
        -#define JB_R13  3
        -#define JB_R14  4
        -#define JB_R15  5
        -#define JB_RSP  6
        -#define JB_PC   7
        -
        -        .file "md.S"
        -        .text
        -
        -        /* _st_md_cxt_save(__jmp_buf env) */
        -.globl _st_md_cxt_save
        -        .type _st_md_cxt_save, @function
        -        .align 16
        -_st_md_cxt_save:
        -        /*
        -         * Save registers.
        -         */
        -        movq %rbx, (JB_RBX*8)(%rdi)
        -        movq %rbp, (JB_RBP*8)(%rdi)
        -        movq %r12, (JB_R12*8)(%rdi)
        -        movq %r13, (JB_R13*8)(%rdi)
        -        movq %r14, (JB_R14*8)(%rdi)
        -        movq %r15, (JB_R15*8)(%rdi)
        -        /* Save SP */
        -        leaq 8(%rsp), %rdx
        -        movq %rdx, (JB_RSP*8)(%rdi)
        -        /* Save PC we are returning to */
        -        movq (%rsp), %rax
        -        movq %rax, (JB_PC*8)(%rdi)
        -        xorq %rax, %rax
        -        ret
        -        .size _st_md_cxt_save, .-_st_md_cxt_save
        -
        -
        -/****************************************************************/
        -
        -        /* _st_md_cxt_restore(__jmp_buf env, int val) */
        -.globl _st_md_cxt_restore
        -        .type _st_md_cxt_restore, @function
        -        .align 16
        -_st_md_cxt_restore:
        -        /*
        -         * Restore registers.
        -         */
        -        movq (JB_RBX*8)(%rdi), %rbx
        -        movq (JB_RBP*8)(%rdi), %rbp
        -        movq (JB_R12*8)(%rdi), %r12
        -        movq (JB_R13*8)(%rdi), %r13
        -        movq (JB_R14*8)(%rdi), %r14
        -        movq (JB_R15*8)(%rdi), %r15
        -        /* Set return value */
        -        test %esi, %esi
        -        mov $01, %eax
        -        cmove %eax, %esi
        -        mov %esi, %eax
        -        movq (JB_PC*8)(%rdi), %rdx
        -        movq (JB_RSP*8)(%rdi), %rsp
        -        /* Jump to saved PC */
        -        jmpq *%rdx
        -        .size _st_md_cxt_restore, .-_st_md_cxt_restore
        -
        -/****************************************************************/
        -
        -#endif
        -
        diff --git a/trunk/research/st/md.h b/trunk/research/st/md.h
        deleted file mode 100644
        index bf82f4d55..000000000
        --- a/trunk/research/st/md.h
        +++ /dev/null
        @@ -1,193 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -/*
        - * This file is derived directly from Netscape Communications Corporation,
        - * and consists of extensive modifications made during the year(s) 1999-2000.
        - */
        -
        -#ifndef __ST_MD_H__
        -#define __ST_MD_H__
        -
        -#if defined(ETIMEDOUT) && !defined(ETIME)
        -    #define ETIME ETIMEDOUT
        -#endif
        -
        -#if defined(MAP_ANONYMOUS) && !defined(MAP_ANON)
        -    #define MAP_ANON MAP_ANONYMOUS
        -#endif
        -
        -#ifndef MAP_FAILED
        -    #define MAP_FAILED -1
        -#endif
        -
        -/*****************************************
        - * Platform specifics
        - */
        -#if defined (LINUX)
        -    /* linux ok, defined bellow */
        -#elif defined (AIX)
        -    #error "AIX not supported"
        -#elif defined (CYGWIN)
        -    #error "CYGWIN not supported"
        -#elif defined (DARWIN)
        -    #error "DARWIN not supported"
        -#elif defined (FREEBSD)
        -    #error "FREEBSD not supported"
        -#elif defined (HPUX)
        -    #error "HPUX not supported"
        -#elif defined (IRIX)
        -    #error "IRIX not supported"
        -#elif defined (NETBSD)
        -    #error "NETBSD not supported"
        -#elif defined (OPENBSD)
        -    #error "OPENBSD not supported"
        -#elif defined (OSF1)
        -    #error "OSF1 not supported"
        -#elif defined (SOLARIS)
        -    #error "SOLARIS not supported"
        -#else
        -    #error "Unknown OS"
        -#endif /* OS */
        -
        -/* linux only, defined bellow */
        -/*
        - * These are properties of the linux kernel and are the same on every
        - * flavor and architecture.
        - */
        -#define MD_USE_BSD_ANON_MMAP
        -#define MD_ACCEPT_NB_NOT_INHERITED
        -#define MD_ALWAYS_UNSERIALIZED_ACCEPT
        -/*
        - * Modern GNU/Linux is Posix.1g compliant.
        - */
        -#define MD_HAVE_SOCKLEN_T
        -
        -/*
        - * All architectures and flavors of linux have the gettimeofday
        - * function but if you know of a faster way, use it.
        - */
        -#define MD_GET_UTIME()            \
        -    struct timeval tv;              \
        -    (void) gettimeofday(&tv, NULL); \
        -    return (tv.tv_sec * 1000000LL + tv.tv_usec)
        -
        -#if defined(__mips__)
        -    #define MD_STACK_GROWS_DOWN
        -#else /* Not or mips */
        -    /*
        -     * On linux, there are a few styles of jmpbuf format.  These vary based
        -     * on architecture/glibc combination.
        -     *
        -     * Most of the glibc based toggles were lifted from:
        -     * mozilla/nsprpub/pr/include/md/_linux.h
        -     */
        -    /*
        -     * Starting with glibc 2.4, JB_SP definitions are not public anymore.
        -     * They, however, can still be found in glibc source tree in
        -     * architecture-specific "jmpbuf-offsets.h" files.
        -     * Most importantly, the content of jmp_buf is mangled by setjmp to make
        -     * it completely opaque (the mangling can be disabled by setting the
        -     * LD_POINTER_GUARD environment variable before application execution).
        -     * Therefore we will use built-in _st_md_cxt_save/_st_md_cxt_restore
        -     * functions as a setjmp/longjmp replacement wherever they are available
        -     * unless USE_LIBC_SETJMP is defined.
        -     */
        -    #if defined(__i386__)
        -        #define MD_STACK_GROWS_DOWN
        -        #define MD_USE_BUILTIN_SETJMP
        -        
        -        #if defined(__GLIBC__) && __GLIBC__ >= 2
        -            #ifndef JB_SP
        -                #define JB_SP 4
        -            #endif
        -            #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_SP]
        -        #else
        -            /* not an error but certainly cause for caution */
        -            #error "Untested use of old glibc on i386"
        -            #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[0].__sp
        -        #endif
        -    #elif defined(__amd64__) || defined(__x86_64__)
        -        #define MD_STACK_GROWS_DOWN
        -        #define MD_USE_BUILTIN_SETJMP
        -        
        -        #ifndef JB_RSP
        -            #define JB_RSP 6
        -        #endif
        -        #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[JB_RSP]
        -    #elif defined(__arm__)
        -        #define MD_STACK_GROWS_DOWN
        -        
        -        #if defined(__GLIBC__) && __GLIBC__ >= 2
        -            #define MD_GET_SP(_t) (_t)->context[0].__jmpbuf[8]
        -        #else
        -            #error "ARM/Linux pre-glibc2 not supported yet"
        -        #endif /* defined(__GLIBC__) && __GLIBC__ >= 2 */
        -    #else
        -        #error "Unknown CPU architecture"
        -    #endif /* Cases with common MD_INIT_CONTEXT and different SP locations */
        -#endif /* Cases with different MD_INIT_CONTEXT */
        -
        -#if defined(MD_USE_BUILTIN_SETJMP) && !defined(USE_LIBC_SETJMP)
        -    /* i386/x86_64 */
        -    #define MD_SETJMP(env) _st_md_cxt_save(env)
        -    #define MD_LONGJMP(env, val) _st_md_cxt_restore(env, val)
        -    
        -    extern int _st_md_cxt_save(jmp_buf env);
        -    extern void _st_md_cxt_restore(jmp_buf env, int val);
        -#else
        -    /* arm/mips */
        -    #define MD_SETJMP(env) setjmp(env)
        -    #define MD_LONGJMP(env, val) longjmp(env, val)
        -#endif
        -
        -/*****************************************
        - * Other defines
        - */
        -#ifndef MD_STACK_PAD_SIZE
        -    #define MD_STACK_PAD_SIZE 128
        -#endif
        -
        -#if !defined(MD_HAVE_SOCKLEN_T) && !defined(socklen_t)
        -    #define socklen_t int
        -#endif
        -
        -#ifndef MD_CAP_STACK
        -    #define MD_CAP_STACK(var_addr)
        -#endif
        -
        -#endif /* !__ST_MD_H__ */
        -
        diff --git a/trunk/research/st/public.h b/trunk/research/st/public.h
        deleted file mode 100644
        index 3275191bc..000000000
        --- a/trunk/research/st/public.h
        +++ /dev/null
        @@ -1,164 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -#ifndef __ST_THREAD_H__
        -#define __ST_THREAD_H__
        -
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -
        -#define ST_VERSION        "1.9"
        -#define ST_VERSION_MAJOR    1
        -#define ST_VERSION_MINOR    9
        -
        -/* Undefine this to remove the context switch callback feature. */
        -#define ST_SWITCH_CB
        -
        -#ifndef ETIME
        -    #define ETIME ETIMEDOUT
        -#endif
        -
        -#ifndef ST_UTIME_NO_TIMEOUT
        -    #define ST_UTIME_NO_TIMEOUT ((st_utime_t) -1LL)
        -#endif
        -
        -#ifndef ST_UTIME_NO_WAIT
        -    #define ST_UTIME_NO_WAIT 0
        -#endif
        -
        -#define ST_EVENTSYS_DEFAULT 0
        -#define ST_EVENTSYS_SELECT  1
        -#define ST_EVENTSYS_POLL    2
        -#define ST_EVENTSYS_ALT     3
        -
        -#ifdef __cplusplus
        -extern "C" {
        -#endif
        -    typedef unsigned long long  st_utime_t;
        -    typedef struct _st_thread * st_thread_t;
        -    typedef struct _st_cond *   st_cond_t;
        -    typedef struct _st_mutex *  st_mutex_t;
        -    typedef struct _st_netfd *  st_netfd_t;
        -    #ifdef ST_SWITCH_CB
        -    typedef void (*st_switch_cb_t)(void);
        -    #endif
        -    
        -    extern int st_init(void);
        -    extern int st_getfdlimit(void);
        -    
        -    extern int st_set_eventsys(int eventsys);
        -    extern int st_get_eventsys(void);
        -    extern const char *st_get_eventsys_name(void);
        -    
        -    #ifdef ST_SWITCH_CB
        -    extern st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb);
        -    extern st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb);
        -    #endif
        -    
        -    extern st_thread_t st_thread_self(void);
        -    extern void st_thread_exit(void *retval);
        -    extern int st_thread_join(st_thread_t trd, void **retvalp);
        -    extern void st_thread_interrupt(st_thread_t trd);
        -    extern st_thread_t st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stack_size);
        -    extern int st_randomize_stacks(int on);
        -    extern int st_set_utime_function(st_utime_t (*func)(void));
        -    
        -    extern st_utime_t st_utime(void);
        -    extern st_utime_t st_utime_last_clock(void);
        -    extern int st_timecache_set(int on);
        -    extern time_t st_time(void);
        -    extern int st_usleep(st_utime_t usecs);
        -    extern int st_sleep(int secs);
        -    extern st_cond_t st_cond_new(void);
        -    extern int st_cond_destroy(st_cond_t cvar);
        -    extern int st_cond_timedwait(st_cond_t cvar, st_utime_t timeout);
        -    extern int st_cond_wait(st_cond_t cvar);
        -    extern int st_cond_signal(st_cond_t cvar);
        -    extern int st_cond_broadcast(st_cond_t cvar);
        -    extern st_mutex_t st_mutex_new(void);
        -    extern int st_mutex_destroy(st_mutex_t lock);
        -    extern int st_mutex_lock(st_mutex_t lock);
        -    extern int st_mutex_unlock(st_mutex_t lock);
        -    extern int st_mutex_trylock(st_mutex_t lock);
        -    
        -    extern int st_key_create(int *keyp, void (*destructor)(void *));
        -    extern int st_key_getlimit(void);
        -    extern int st_thread_setspecific(int key, void *value);
        -    extern void *st_thread_getspecific(int key);
        -    
        -    extern st_netfd_t st_netfd_open(int osfd);
        -    extern st_netfd_t st_netfd_open_socket(int osfd);
        -    extern void st_netfd_free(st_netfd_t fd);
        -    extern int st_netfd_close(st_netfd_t fd);
        -    extern int st_netfd_fileno(st_netfd_t fd);
        -    extern void st_netfd_setspecific(st_netfd_t fd, void *value, void (*destructor)(void *));
        -    extern void *st_netfd_getspecific(st_netfd_t fd);
        -    extern int st_netfd_serialize_accept(st_netfd_t fd);
        -    extern int st_netfd_poll(st_netfd_t fd, int how, st_utime_t timeout);
        -    
        -    extern int st_poll(struct pollfd *pds, int npds, st_utime_t timeout);
        -    extern st_netfd_t st_accept(st_netfd_t fd, struct sockaddr *addr, int *addrlen, st_utime_t timeout);
        -    extern int st_connect(st_netfd_t fd, const struct sockaddr *addr, int addrlen, st_utime_t timeout);
        -    extern ssize_t st_read(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
        -    extern ssize_t st_read_fully(st_netfd_t fd, void *buf, size_t nbyte, st_utime_t timeout);
        -    extern int st_read_resid(st_netfd_t fd, void *buf, size_t *resid, st_utime_t timeout);
        -    extern ssize_t st_readv(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout);
        -    extern int st_readv_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout);
        -    extern ssize_t st_write(st_netfd_t fd, const void *buf, size_t nbyte, st_utime_t timeout);
        -    extern int st_write_resid(st_netfd_t fd, const void *buf, size_t *resid, st_utime_t timeout);
        -    extern ssize_t st_writev(st_netfd_t fd, const struct iovec *iov, int iov_size, st_utime_t timeout);
        -    extern int st_writev_resid(st_netfd_t fd, struct iovec **iov, int *iov_size, st_utime_t timeout);
        -    extern int st_recvfrom(st_netfd_t fd, void *buf, int len, struct sockaddr *from, int *fromlen, st_utime_t timeout);
        -    extern int st_sendto(st_netfd_t fd, const void *msg, int len, const struct sockaddr *to, int tolen, st_utime_t timeout);
        -    extern int st_recvmsg(st_netfd_t fd, struct msghdr *msg, int flags, st_utime_t timeout);
        -    extern int st_sendmsg(st_netfd_t fd, const struct msghdr *msg, int flags, st_utime_t timeout);
        -    extern st_netfd_t st_open(const char *path, int oflags, mode_t mode);
        -    
        -    #ifdef DEBUG
        -    extern void _st_show_thread_stack(st_thread_t thread, const char *messg);
        -    extern void _st_iterate_threads(void);
        -    #endif
        -#ifdef __cplusplus
        -}
        -#endif
        -
        -#endif /* !__ST_THREAD_H__ */
        -
        diff --git a/trunk/research/st/sched.c b/trunk/research/st/sched.c
        deleted file mode 100755
        index 66095cfd1..000000000
        --- a/trunk/research/st/sched.c
        +++ /dev/null
        @@ -1,680 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -/*
        - * This file is derived directly from Netscape Communications Corporation,
        - * and consists of extensive modifications made during the year(s) 1999-2000.
        - */
        -
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include "common.h"
        -
        -/* Global data */
        -_st_vp_t _st_this_vp;           /* This VP */
        -_st_thread_t *_st_this_thread;  /* Current thread */
        -int _st_active_count = 0;       /* Active thread count */
        -
        -time_t _st_curr_time = 0;       /* Current time as returned by time(2) */
        -st_utime_t _st_last_tset;       /* Last time it was fetched */
        -
        -int st_poll(struct pollfd *pds, int npds, st_utime_t timeout)
        -{
        -    struct pollfd *pd;
        -    struct pollfd *epd = pds + npds;
        -    _st_pollq_t pq;
        -    _st_thread_t *me = _ST_CURRENT_THREAD();
        -    int n;
        -    
        -    if (me->flags & _ST_FL_INTERRUPT) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        return -1;
        -    }
        -    
        -    if ((*_st_eventsys->pollset_add)(pds, npds) < 0) {
        -        return -1;
        -    }
        -    
        -    pq.pds = pds;
        -    pq.npds = npds;
        -    pq.thread = me;
        -    pq.on_ioq = 1;
        -    _ST_ADD_IOQ(pq);
        -    if (timeout != ST_UTIME_NO_TIMEOUT) {
        -        _ST_ADD_SLEEPQ(me, timeout);
        -    }
        -    me->state = _ST_ST_IO_WAIT;
        -    
        -    _ST_SWITCH_CONTEXT(me);
        -    
        -    n = 0;
        -    if (pq.on_ioq) {
        -        /* If we timed out, the pollq might still be on the ioq. Remove it */
        -        _ST_DEL_IOQ(pq);
        -        (*_st_eventsys->pollset_del)(pds, npds);
        -    } else {
        -        /* Count the number of ready descriptors */
        -        for (pd = pds; pd < epd; pd++) {
        -            if (pd->revents) {
        -                n++;
        -            }
        -        }
        -    }
        -    
        -    if (me->flags & _ST_FL_INTERRUPT) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        return -1;
        -    }
        -    
        -    return n;
        -}
        -
        -void _st_vp_schedule(void)
        -{
        -    _st_thread_t *trd;
        -    
        -    if (_ST_RUNQ.next != &_ST_RUNQ) {
        -        /* Pull thread off of the run queue */
        -        trd = _ST_THREAD_PTR(_ST_RUNQ.next);
        -        _ST_DEL_RUNQ(trd);
        -    } else {
        -        /* If there are no threads to run, switch to the idle thread */
        -        trd = _st_this_vp.idle_thread;
        -    }
        -    ST_ASSERT(trd->state == _ST_ST_RUNNABLE);
        -    
        -    /* Resume the thread */
        -    trd->state = _ST_ST_RUNNING;
        -    _ST_RESTORE_CONTEXT(trd);
        -}
        -
        -/*
        - * Initialize this Virtual Processor
        - */
        -int st_init(void)
        -{
        -    _st_thread_t *trd;
        -    
        -    if (_st_active_count) {
        -        /* Already initialized */
        -        return 0;
        -    }
        -    
        -    /* We can ignore return value here */
        -    st_set_eventsys(ST_EVENTSYS_DEFAULT);
        -    
        -    if (_st_io_init() < 0) {
        -        return -1;
        -    }
        -    
        -    memset(&_st_this_vp, 0, sizeof(_st_vp_t));
        -    
        -    ST_INIT_CLIST(&_ST_RUNQ);
        -    ST_INIT_CLIST(&_ST_IOQ);
        -    ST_INIT_CLIST(&_ST_ZOMBIEQ);
        -#ifdef DEBUG
        -    ST_INIT_CLIST(&_ST_THREADQ);
        -#endif
        -    
        -    if ((*_st_eventsys->init)() < 0) {
        -        return -1;
        -    }
        -    
        -    _st_this_vp.pagesize = getpagesize();
        -    _st_this_vp.last_clock = st_utime();
        -    
        -    /*
        -    * Create idle thread
        -    */
        -    _st_this_vp.idle_thread = st_thread_create(_st_idle_thread_start, NULL, 0, 0);
        -    if (!_st_this_vp.idle_thread) {
        -        return -1;
        -    }
        -    _st_this_vp.idle_thread->flags = _ST_FL_IDLE_THREAD;
        -    _st_active_count--;
        -    _ST_DEL_RUNQ(_st_this_vp.idle_thread);
        -    
        -    /*
        -    * Initialize primordial thread
        -    */
        -    trd = (_st_thread_t *) calloc(1, sizeof(_st_thread_t) +
        -    (ST_KEYS_MAX * sizeof(void *)));
        -    if (!trd) {
        -        return -1;
        -    }
        -    trd->private_data = (void **) (trd + 1);
        -    trd->state = _ST_ST_RUNNING;
        -    trd->flags = _ST_FL_PRIMORDIAL;
        -    _ST_SET_CURRENT_THREAD(trd);
        -    _st_active_count++;
        -#ifdef DEBUG
        -    _ST_ADD_THREADQ(trd);
        -#endif
        -    
        -    return 0;
        -}
        -
        -#ifdef ST_SWITCH_CB
        -st_switch_cb_t st_set_switch_in_cb(st_switch_cb_t cb)
        -{
        -    st_switch_cb_t ocb = _st_this_vp.switch_in_cb;
        -    _st_this_vp.switch_in_cb = cb;
        -    return ocb;
        -}
        -
        -st_switch_cb_t st_set_switch_out_cb(st_switch_cb_t cb)
        -{
        -    st_switch_cb_t ocb = _st_this_vp.switch_out_cb;
        -    _st_this_vp.switch_out_cb = cb;
        -    return ocb;
        -}
        -#endif
        -
        -/*
        - * Start function for the idle thread
        - */
        -/* ARGSUSED */
        -void *_st_idle_thread_start(void *arg)
        -{
        -    _st_thread_t *me = _ST_CURRENT_THREAD();
        -    
        -    while (_st_active_count > 0) {
        -        /* Idle vp till I/O is ready or the smallest timeout expired */
        -        _ST_VP_IDLE();
        -        
        -        /* Check sleep queue for expired threads */
        -        _st_vp_check_clock();
        -        
        -        me->state = _ST_ST_RUNNABLE;
        -        _ST_SWITCH_CONTEXT(me);
        -    }
        -    
        -    /* No more threads */
        -    exit(0);
        -    
        -    /* NOTREACHED */
        -    return NULL;
        -}
        -
        -void st_thread_exit(void *retval)
        -{
        -    _st_thread_t *trd = _ST_CURRENT_THREAD();
        -    
        -    trd->retval = retval;
        -    _st_thread_cleanup(trd);
        -    _st_active_count--;
        -    if (trd->term) {
        -        /* Put thread on the zombie queue */
        -        trd->state = _ST_ST_ZOMBIE;
        -        _ST_ADD_ZOMBIEQ(trd);
        -        
        -        /* Notify on our termination condition variable */
        -        st_cond_signal(trd->term);
        -        
        -        /* Switch context and come back later */
        -        _ST_SWITCH_CONTEXT(trd);
        -        
        -        /* Continue the cleanup */
        -        st_cond_destroy(trd->term);
        -        trd->term = NULL;
        -    }
        -    
        -#ifdef DEBUG
        -    _ST_DEL_THREADQ(trd);
        -#endif
        -    
        -    if (!(trd->flags & _ST_FL_PRIMORDIAL)) {
        -        _st_stack_free(trd->stack);
        -    }
        -    
        -    /* Find another thread to run */
        -    _ST_SWITCH_CONTEXT(trd);
        -    /* Not going to land here */
        -}
        -
        -int st_thread_join(_st_thread_t *trd, void **retvalp)
        -{
        -    _st_cond_t *term = trd->term;
        -    
        -    /* Can't join a non-joinable thread */
        -    if (term == NULL) {
        -        errno = EINVAL;
        -        return -1;
        -    }
        -    if (_ST_CURRENT_THREAD() == trd) {
        -        errno = EDEADLK;
        -        return -1;
        -    }
        -    
        -    /* Multiple threads can't wait on the same joinable thread */
        -    if (term->wait_q.next != &term->wait_q) {
        -        errno = EINVAL;
        -        return -1;
        -    }
        -    
        -    while (trd->state != _ST_ST_ZOMBIE) {
        -        if (st_cond_timedwait(term, ST_UTIME_NO_TIMEOUT) != 0) {
        -            return -1;
        -        }
        -    }
        -    
        -    if (retvalp) {
        -        *retvalp = trd->retval;
        -    }
        -    
        -    /*
        -    * Remove target thread from the zombie queue and make it runnable.
        -    * When it gets scheduled later, it will do the clean up.
        -    */
        -    trd->state = _ST_ST_RUNNABLE;
        -    _ST_DEL_ZOMBIEQ(trd);
        -    _ST_ADD_RUNQ(trd);
        -    
        -    return 0;
        -}
        -
        -void _st_thread_main(void)
        -{
        -    _st_thread_t *trd = _ST_CURRENT_THREAD();
        -    
        -    /*
        -    * Cap the stack by zeroing out the saved return address register
        -    * value. This allows some debugging/profiling tools to know when
        -    * to stop unwinding the stack. It's a no-op on most platforms.
        -    */
        -    MD_CAP_STACK(&trd);
        -    
        -    /* Run thread main */
        -    trd->retval = (*trd->start)(trd->arg);
        -    
        -    /* All done, time to go away */
        -    st_thread_exit(trd->retval);
        -}
        -
        -/*
        - * Insert "thread" into the timeout heap, in the position
        - * specified by thread->heap_index.  See docs/timeout_heap.txt
        - * for details about the timeout heap.
        - */
        -static _st_thread_t **heap_insert(_st_thread_t *trd)
        -{
        -    int target = trd->heap_index;
        -    int s = target;
        -    _st_thread_t **p = &_ST_SLEEPQ;
        -    int bits = 0;
        -    int bit;
        -    int index = 1;
        -    
        -    while (s) {
        -        s >>= 1;
        -        bits++;
        -    }
        -    
        -    for (bit = bits - 2; bit >= 0; bit--) {
        -        if (trd->due < (*p)->due) {
        -            _st_thread_t *t = *p;
        -            trd->left = t->left;
        -            trd->right = t->right;
        -            *p = trd;
        -            trd->heap_index = index;
        -            trd = t;
        -        }
        -        index <<= 1;
        -        if (target & (1 << bit)) {
        -            p = &((*p)->right);
        -            index |= 1;
        -        } else {
        -            p = &((*p)->left);
        -        }
        -    }
        -    
        -    trd->heap_index = index;
        -    *p = trd;
        -    trd->left = trd->right = NULL;
        -    
        -    return p;
        -}
        -
        -/*
        - * Delete "thread" from the timeout heap.
        - */
        -static void heap_delete(_st_thread_t *trd) 
        -{
        -    _st_thread_t *t, **p;
        -    int bits = 0;
        -    int s, bit;
        -    
        -    /* First find and unlink the last heap element */
        -    p = &_ST_SLEEPQ;
        -    s = _ST_SLEEPQ_SIZE;
        -    while (s) {
        -        s >>= 1;
        -        bits++;
        -    }
        -    
        -    for (bit = bits - 2; bit >= 0; bit--) {
        -        if (_ST_SLEEPQ_SIZE & (1 << bit)) {
        -            p = &((*p)->right);
        -        } else {
        -            p = &((*p)->left);
        -        }
        -    }
        -    
        -    t = *p;
        -    *p = NULL;
        -    --_ST_SLEEPQ_SIZE;
        -    if (t != trd) {
        -        /*
        -        * Insert the unlinked last element in place of the element we are deleting
        -        */
        -        t->heap_index = trd->heap_index;
        -        p = heap_insert(t);
        -        t = *p;
        -        t->left = trd->left;
        -        t->right = trd->right;
        -        
        -        /*
        -        * Reestablish the heap invariant.
        -        */
        -        for (;;) {
        -            _st_thread_t *y; /* The younger child */
        -            int index_tmp;
        -            
        -            if (t->left == NULL) {
        -                break;
        -            } else if (t->right == NULL) {
        -                y = t->left;
        -            } else if (t->left->due < t->right->due) {
        -                y = t->left;
        -            } else {
        -                y = t->right;
        -            }
        -            
        -            if (t->due > y->due) {
        -                _st_thread_t *tl = y->left;
        -                _st_thread_t *tr = y->right;
        -                *p = y;
        -                if (y == t->left) {
        -                    y->left = t;
        -                    y->right = t->right;
        -                    p = &y->left;
        -                } else {
        -                    y->left = t->left;
        -                    y->right = t;
        -                    p = &y->right;
        -                }
        -                t->left = tl;
        -                t->right = tr;
        -                index_tmp = t->heap_index;
        -                t->heap_index = y->heap_index;
        -                y->heap_index = index_tmp;
        -            } else {
        -                break;
        -            }
        -        }
        -    }
        -    
        -    trd->left = trd->right = NULL;
        -}
        -
        -void _st_add_sleep_q(_st_thread_t *trd, st_utime_t timeout)
        -{
        -    trd->due = _ST_LAST_CLOCK + timeout;
        -    trd->flags |= _ST_FL_ON_SLEEPQ;
        -    trd->heap_index = ++_ST_SLEEPQ_SIZE;
        -    heap_insert(trd);
        -}
        -
        -void _st_del_sleep_q(_st_thread_t *trd)
        -{
        -    heap_delete(trd);
        -    trd->flags &= ~_ST_FL_ON_SLEEPQ;
        -}
        -
        -void _st_vp_check_clock(void)
        -{
        -    _st_thread_t *trd;
        -    st_utime_t elapsed, now;
        -    
        -    now = st_utime();
        -    elapsed = now - _ST_LAST_CLOCK;
        -    _ST_LAST_CLOCK = now;
        -    
        -    if (_st_curr_time && now - _st_last_tset > 999000) {
        -        _st_curr_time = time(NULL);
        -        _st_last_tset = now;
        -    }
        -    
        -    while (_ST_SLEEPQ != NULL) {
        -        trd = _ST_SLEEPQ;
        -        ST_ASSERT(trd->flags & _ST_FL_ON_SLEEPQ);
        -        if (trd->due > now) {
        -            break;
        -        }
        -        _ST_DEL_SLEEPQ(trd);
        -        
        -        /* If thread is waiting on condition variable, set the time out flag */
        -        if (trd->state == _ST_ST_COND_WAIT) {
        -            trd->flags |= _ST_FL_TIMEDOUT;
        -        }
        -        
        -        /* Make thread runnable */
        -        ST_ASSERT(!(trd->flags & _ST_FL_IDLE_THREAD));
        -        trd->state = _ST_ST_RUNNABLE;
        -        _ST_ADD_RUNQ(trd);
        -    }
        -}
        -
        -void st_thread_interrupt(_st_thread_t* trd)
        -{
        -    /* If thread is already dead */
        -    if (trd->state == _ST_ST_ZOMBIE) {
        -        return;
        -    }
        -    
        -    trd->flags |= _ST_FL_INTERRUPT;
        -    
        -    if (trd->state == _ST_ST_RUNNING || trd->state == _ST_ST_RUNNABLE) {
        -        return;
        -    }
        -    
        -    if (trd->flags & _ST_FL_ON_SLEEPQ) {
        -        _ST_DEL_SLEEPQ(trd);
        -    }
        -    
        -    /* Make thread runnable */
        -    trd->state = _ST_ST_RUNNABLE;
        -    _ST_ADD_RUNQ(trd);
        -}
        -
        -_st_thread_t *st_thread_create(void *(*start)(void *arg), void *arg, int joinable, int stk_size)
        -{
        -    _st_thread_t *trd;
        -    _st_stack_t *stack;
        -    void **ptds;
        -    char *sp;
        -    
        -    /* Adjust stack size */
        -    if (stk_size == 0) {
        -        stk_size = ST_DEFAULT_STACK_SIZE;
        -    }
        -    stk_size = ((stk_size + _ST_PAGE_SIZE - 1) / _ST_PAGE_SIZE) * _ST_PAGE_SIZE;
        -    stack = _st_stack_new(stk_size);
        -    if (!stack) {
        -        return NULL;
        -    }
        -    
        -    /* Allocate thread object and per-thread data off the stack */
        -#if defined (MD_STACK_GROWS_DOWN)
        -    sp = stack->stk_top;
        -    /*
        -    * The stack segment is split in the middle. The upper half is used
        -    * as backing store for the register stack which grows upward.
        -    * The lower half is used for the traditional memory stack which
        -    * grows downward. Both stacks start in the middle and grow outward
        -    * from each other.
        -    */
        -    /**
        -    The below comments is by winlin:
        -    The Stack public structure:
        -        +--------------------------------------------------------------+
        -        |                         stack                                |
        -        +--------------------------------------------------------------+
        -       bottom                                                         top
        -    The code bellow use the stack as:
        -        +-----------------+-----------------+-------------+------------+
        -        | stack of thread |pad+align(128B+) |thread(336B) | keys(128B) |
        -        +-----------------+-----------------+-------------+------------+
        -       bottom            sp                trd           ptds         top
        -               (context[0].__jmpbuf.sp)             (private_data)
        -    */
        -    sp = sp - (ST_KEYS_MAX * sizeof(void *));
        -    ptds = (void **) sp;
        -    sp = sp - sizeof(_st_thread_t);
        -    trd = (_st_thread_t *) sp;
        -    
        -    /* Make stack 64-byte aligned */
        -    if ((unsigned long)sp & 0x3f) {
        -        sp = sp - ((unsigned long)sp & 0x3f);
        -    }
        -    stack->sp = sp - _ST_STACK_PAD_SIZE;
        -#else
        -    #error "Only Supports Stack Grown Down"
        -#endif
        -    
        -    memset(trd, 0, sizeof(_st_thread_t));
        -    memset(ptds, 0, ST_KEYS_MAX * sizeof(void *));
        -    
        -    /* Initialize thread */
        -    trd->private_data = ptds;
        -    trd->stack = stack;
        -    trd->start = start;
        -    trd->arg = arg;
        -
        -// by winlin, expand macro MD_INIT_CONTEXT
        -#if defined(__mips__)
        -    MD_SETJMP((trd)->context);
        -    trd->context[0].__jmpbuf[0].__pc = (__ptr_t) _st_thread_main;
        -    trd->context[0].__jmpbuf[0].__sp = stack->sp;
        -#else
        -    if (MD_SETJMP((trd)->context)) {
        -        _st_thread_main();
        -    }
        -    MD_GET_SP(trd) = (long) (stack->sp);
        -#endif
        -    
        -    /* If thread is joinable, allocate a termination condition variable */
        -    if (joinable) {
        -        trd->term = st_cond_new();
        -        if (trd->term == NULL) {
        -            _st_stack_free(trd->stack);
        -            return NULL;
        -        }
        -    }
        -    
        -    /* Make thread runnable */
        -    trd->state = _ST_ST_RUNNABLE;
        -    _st_active_count++;
        -    _ST_ADD_RUNQ(trd);
        -#ifdef DEBUG
        -    _ST_ADD_THREADQ(trd);
        -#endif
        -    
        -    return trd;
        -}
        -
        -_st_thread_t *st_thread_self(void)
        -{
        -    return _ST_CURRENT_THREAD();
        -}
        -
        -#ifdef DEBUG
        -/* ARGSUSED */
        -void _st_show_thread_stack(_st_thread_t *trd, const char *messg)
        -{
        -}
        -
        -/* To be set from debugger */
        -int _st_iterate_threads_flag = 0;
        -
        -void _st_iterate_threads(void)
        -{
        -    static _st_thread_t *trd = NULL;
        -    static jmp_buf orig_jb, save_jb;
        -    _st_clist_t *q;
        -    
        -    if (!_st_iterate_threads_flag) {
        -        if (trd) {
        -            memcpy(trd->context, save_jb, sizeof(jmp_buf));
        -            MD_LONGJMP(orig_jb, 1);
        -        }
        -        return;
        -    }
        -    
        -    if (trd) {
        -        memcpy(trd->context, save_jb, sizeof(jmp_buf));
        -        _st_show_thread_stack(trd, NULL);
        -    } else {
        -        if (MD_SETJMP(orig_jb)) {
        -            _st_iterate_threads_flag = 0;
        -            trd = NULL;
        -            _st_show_thread_stack(trd, "Iteration completed");
        -            return;
        -        }
        -        trd = _ST_CURRENT_THREAD();
        -        _st_show_thread_stack(trd, "Iteration started");
        -    }
        -    
        -    q = trd->tlink.next;
        -    if (q == &_ST_THREADQ) {
        -        q = q->next;
        -    }
        -    ST_ASSERT(q != &_ST_THREADQ);
        -    trd = _ST_THREAD_THREADQ_PTR(q);
        -    if (trd == _ST_CURRENT_THREAD()) {
        -        MD_LONGJMP(orig_jb, 1);
        -    }
        -    memcpy(save_jb, trd->context, sizeof(jmp_buf));
        -    MD_LONGJMP(trd->context, 1);
        -}
        -#endif /* DEBUG */
        -
        diff --git a/trunk/research/st/srs.c b/trunk/research/st/srs.c
        deleted file mode 100644
        index 09bc5eacc..000000000
        --- a/trunk/research/st/srs.c
        +++ /dev/null
        @@ -1,497 +0,0 @@
        -#include 
        -#include 
        -#include 
        -
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -
        -#include "public.h"
        -
        -#define srs_trace(msg, ...)   printf(msg, ##__VA_ARGS__);printf("\n")
        -
        -int io_port = 1990;
        -int sleep_ms = 100;
        -
        -void stack_print(long int previous_sp, int level)
        -{
        -    if (level <= 0) {
        -        return;
        -    }
        -    
        -    register long int rsp asm("sp");
        -    char buf[level * 1024];
        -    
        -    stack_print(rsp, level - 1);
        -    
        -    srs_trace("%d. psp=%#lx, sp=%#lx, size=%dB(%dB+%dKB)", 
        -        level, previous_sp, rsp, (int)(previous_sp - rsp), 
        -        (int)(previous_sp - rsp - sizeof(buf)), (int)(sizeof(buf) / 1024));
        -}
        -
        -int huge_stack_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("huge_stack test: start");
        -    
        -    register long int rsp asm("sp");
        -    stack_print(rsp, 10);
        -    
        -    srs_trace("huge_stack test: end");
        -    
        -    return 0;
        -}
        -
        -int sleep_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("sleep test: start");
        -    
        -    srs_trace("1. sleep...");
        -    st_utime_t start = st_utime();
        -    st_usleep(sleep_ms * 1000);
        -    st_utime_t end = st_utime();
        -    
        -    srs_trace("2. sleep ok, sleep=%dus, deviation=%dus", 
        -        (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
        -
        -    srs_trace("sleep test: end");
        -    
        -    return 0;
        -}
        -
        -void* sleep2_func0(void* arg)
        -{
        -    int sleep_ms = 100;
        -    st_utime_t start = st_utime();
        -    st_usleep(sleep_ms * 1000);
        -    st_utime_t end = st_utime();
        -    
        -    srs_trace("sleep ok, sleep=%dus, deviation=%dus", 
        -        (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
        -        
        -    return NULL;
        -}
        -
        -void* sleep2_func1(void* arg)
        -{
        -    int sleep_ms = 250;
        -    st_utime_t start = st_utime();
        -    st_usleep(sleep_ms * 1000);
        -    st_utime_t end = st_utime();
        -    
        -    srs_trace("sleep ok, sleep=%dus, deviation=%dus", 
        -        (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
        -        
        -    return NULL;
        -}
        -
        -int sleep2_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("sleep2 test: start");
        -    
        -    st_thread_t trd0 = st_thread_create(sleep2_func0, NULL, 1, 0);
        -    st_thread_t trd1 = st_thread_create(sleep2_func1, NULL, 1, 0);
        -    st_thread_join(trd0, NULL);
        -    st_thread_join(trd1, NULL);
        -
        -    srs_trace("sleep test: end");
        -    
        -    return 0;
        -}
        -
        -st_mutex_t sleep_work_cond = NULL;
        -void* sleep_deviation_func(void* arg)
        -{
        -    st_mutex_lock(sleep_work_cond);
        -    srs_trace("2. work thread start.");
        -
        -    int64_t i;
        -    for (i = 0; i < 3000000000ULL; i++) {
        -    }
        -    
        -    st_mutex_unlock(sleep_work_cond);
        -    srs_trace("3. work thread end.");
        -    
        -    return NULL;
        -}
        -
        -int sleep_deviation_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("sleep deviation test: start");
        -    
        -    sleep_work_cond = st_mutex_new();
        -    
        -    st_thread_create(sleep_deviation_func, NULL, 0, 0);
        -    st_mutex_lock(sleep_work_cond);
        -    
        -    srs_trace("1. sleep...");
        -    st_utime_t start = st_utime();
        -    
        -    // other thread to do some complex work.
        -    st_mutex_unlock(sleep_work_cond);
        -    st_usleep(1000 * 1000);
        -    
        -    st_utime_t end = st_utime();
        -    
        -    srs_trace("4. sleep ok, sleep=%dus, deviation=%dus", 
        -        (int)(sleep_ms * 1000), (int)(end - start - sleep_ms * 1000));
        -
        -    st_mutex_lock(sleep_work_cond);
        -    srs_trace("sleep deviation test: end");
        -    
        -    st_mutex_destroy(sleep_work_cond);
        -    
        -    return 0;
        -}
        -
        -void* thread_func(void* arg)
        -{
        -    srs_trace("1. thread run");
        -    st_usleep(sleep_ms * 1000);
        -    srs_trace("2. thread completed");
        -    return NULL;
        -}
        -
        -int thread_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("thread test: start");
        -    
        -    st_thread_t trd = st_thread_create(thread_func, NULL, 1, 0);
        -    if (trd == NULL) {
        -        srs_trace("st_thread_create failed");
        -        return -1;
        -    }
        -    
        -    st_thread_join(trd, NULL);
        -    srs_trace("3. thread joined");
        -    
        -    srs_trace("thread test: end");
        -    
        -    return 0;
        -}
        -
        -st_mutex_t sync_start = NULL;
        -st_cond_t sync_cond = NULL;
        -st_mutex_t sync_mutex = NULL;
        -st_cond_t sync_end = NULL;
        -
        -void* sync_master(void* arg)
        -{
        -    // wait for main to sync_start this thread.
        -    st_mutex_lock(sync_start);
        -    st_mutex_unlock(sync_start);
        -    
        -    st_usleep(sleep_ms * 1000);
        -    st_cond_signal(sync_cond);
        -    
        -    st_mutex_lock(sync_mutex);
        -    srs_trace("2. st mutex is ok");
        -    st_mutex_unlock(sync_mutex);
        -    
        -    st_usleep(sleep_ms * 1000);
        -    srs_trace("3. st thread is ok");
        -    st_cond_signal(sync_cond);
        -    
        -    return NULL;
        -}
        -
        -void* sync_slave(void* arg)
        -{
        -    // lock mutex to control thread.
        -    st_mutex_lock(sync_mutex);
        -    
        -    // wait for main to sync_start this thread.
        -    st_mutex_lock(sync_start);
        -    st_mutex_unlock(sync_start);
        -    
        -    // wait thread to ready.
        -    st_cond_wait(sync_cond);
        -    srs_trace("1. st cond is ok");
        -    
        -    // release mutex to control thread
        -    st_usleep(sleep_ms * 1000);
        -    st_mutex_unlock(sync_mutex);
        -    
        -    // wait thread to exit.
        -    st_cond_wait(sync_cond);
        -    srs_trace("4. st is ok");
        -    
        -    st_cond_signal(sync_end);
        -    
        -    return NULL;
        -}
        -
        -int sync_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("sync test: start");
        -    
        -    if ((sync_start = st_mutex_new()) == NULL) {
        -        srs_trace("st_mutex_new sync_start failed");
        -        return -1;
        -    }
        -    st_mutex_lock(sync_start);
        -
        -    if ((sync_cond = st_cond_new()) == NULL) {
        -        srs_trace("st_cond_new cond failed");
        -        return -1;
        -    }
        -
        -    if ((sync_end = st_cond_new()) == NULL) {
        -        srs_trace("st_cond_new end failed");
        -        return -1;
        -    }
        -    
        -    if ((sync_mutex = st_mutex_new()) == NULL) {
        -        srs_trace("st_mutex_new mutex failed");
        -        return -1;
        -    }
        -    
        -    if (!st_thread_create(sync_master, NULL, 0, 0)) {
        -        srs_trace("st_thread_create failed");
        -        return -1;
        -    }
        -    
        -    if (!st_thread_create(sync_slave, NULL, 0, 0)) {
        -        srs_trace("st_thread_create failed");
        -        return -1;
        -    }
        -    
        -    // run all threads.
        -    st_mutex_unlock(sync_start);
        -    
        -    st_cond_wait(sync_end);
        -    srs_trace("sync test: end");
        -    
        -    return 0;
        -}
        -
        -void* io_client(void* arg)
        -{
        -    
        -    int fd;
        -    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        -        srs_trace("create linux socket error.");
        -        return NULL;
        -    }
        -    srs_trace("6. client create linux socket success. fd=%d", fd);
        -    
        -    st_netfd_t stfd;
        -    if ((stfd = st_netfd_open_socket(fd)) == NULL){
        -        srs_trace("st_netfd_open_socket open socket failed.");
        -        return NULL;
        -    }
        -    srs_trace("7. client st open socket success. fd=%d", fd);
        -    
        -    struct sockaddr_in addr;
        -    addr.sin_family = AF_INET;
        -    addr.sin_port = htons(io_port);
        -    addr.sin_addr.s_addr = INADDR_ANY;
        -    if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1) {
        -        srs_trace("bind socket error.");
        -        return NULL;
        -    }
        -    
        -    char buf[1024];
        -    if (st_read_fully(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
        -        srs_trace("st_read_fully failed");
        -        return NULL;
        -    }
        -    if (st_write(stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
        -        srs_trace("st_write failed");
        -        return NULL;
        -    }
        -    
        -    st_netfd_close(stfd);
        -    
        -    return NULL;
        -}
        -
        -int io_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("io test: start, port=%d", io_port);
        -    
        -    int fd;
        -    if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        -        srs_trace("create linux socket error.");
        -        return -1;
        -    }
        -    srs_trace("1. server create linux socket success. fd=%d", fd);
        -    
        -    int reuse_socket = 1;
        -    if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
        -        srs_trace("setsockopt reuse-addr error.");
        -        return -1;
        -    }
        -    srs_trace("2. server setsockopt reuse-addr success. fd=%d", fd);
        -    
        -    struct sockaddr_in addr;
        -    addr.sin_family = AF_INET;
        -    addr.sin_port = htons(io_port);
        -    addr.sin_addr.s_addr = INADDR_ANY;
        -    if (bind(fd, (const struct sockaddr*)&addr, sizeof(struct sockaddr_in)) == -1) {
        -        srs_trace("bind socket error.");
        -        return -1;
        -    }
        -    srs_trace("3. server bind socket success. fd=%d", fd);
        -    
        -    if (listen(fd, 10) == -1) {
        -        srs_trace("listen socket error.");
        -        return -1;
        -    }
        -    srs_trace("4. server listen socket success. fd=%d", fd);
        -    
        -    st_netfd_t stfd;
        -    if ((stfd = st_netfd_open_socket(fd)) == NULL){
        -        srs_trace("st_netfd_open_socket open socket failed.");
        -        return -1;
        -    }
        -    srs_trace("5. server st open socket success. fd=%d", fd);
        -    
        -    if (!st_thread_create(io_client, NULL, 0, 0)) {
        -        srs_trace("st_thread_create failed");
        -        return -1;
        -    }
        -    
        -    st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
        -    srs_trace("8. server get a client. fd=%d", st_netfd_fileno(client_stfd));
        -    
        -    char buf[1024];
        -    if (st_write(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
        -        srs_trace("st_write failed");
        -        return -1;
        -    }
        -    if (st_read_fully(client_stfd, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) != sizeof(buf)) {
        -        srs_trace("st_read_fully failed");
        -        return -1;
        -    }
        -    srs_trace("9. server io completed.");
        -    
        -    st_netfd_close(stfd);
        -    st_netfd_close(client_stfd);
        -    
        -    srs_trace("io test: end");
        -    return 0;
        -}
        -
        -int pipe_test()
        -{
        -    srs_trace("===================================================");
        -    srs_trace("pipe test: start");
        -    
        -    int fds[2];
        -    if (pipe(fds) < 0) {
        -        srs_trace("pipe failed");
        -        return -1;
        -    }
        -    srs_trace("1. pipe ok, %d=>%d", fds[1], fds[0]);
        -    
        -    st_netfd_t fdw;
        -    if ((fdw = st_netfd_open_socket(fds[1])) == NULL) {
        -        srs_trace("st_netfd_open_socket open socket failed.");
        -        return -1;
        -    }
        -    srs_trace("2. open write fd ok");
        -    
        -    st_netfd_t fdr;
        -    if ((fdr = st_netfd_open_socket(fds[0])) == NULL) {
        -        srs_trace("st_netfd_open_socket open socket failed.");
        -        return -1;
        -    }
        -    srs_trace("3. open read fd ok");
        -    
        -    char buf[1024];
        -    if (st_write(fdw, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) {
        -        srs_trace("st_write socket failed.");
        -        return -1;
        -    }
        -    srs_trace("4. write to pipe ok");
        -    
        -    if (st_read(fdr, buf, sizeof(buf), ST_UTIME_NO_TIMEOUT) < 0) {
        -        srs_trace("st_read socket failed.");
        -        return -1;
        -    }
        -    srs_trace("5. read from pipe ok");
        -    
        -    st_netfd_close(fdw);
        -    st_netfd_close(fdr);
        -    
        -    srs_trace("pipe test: end");
        -    return 0;
        -}
        -
        -int main(int argc, char** argv)
        -{
        -    srs_trace("ETIME=%d", ETIME);
        -    
        -    if (st_set_eventsys(ST_EVENTSYS_ALT) < 0) {
        -        srs_trace("st_set_eventsys failed");
        -        return -1;
        -    }
        -    
        -    if (st_init() < 0) {
        -        srs_trace("st_init failed");
        -        return -1;
        -    }
        -    
        -    if (sleep2_test() < 0) {
        -        srs_trace("sleep2_test failed");
        -        return -1;
        -    }
        -    
        -    if (sleep_test() < 0) {
        -        srs_trace("sleep_test failed");
        -        return -1;
        -    }
        -    
        -    if (sleep_deviation_test() < 0) {
        -        srs_trace("sleep_deviation_test failed");
        -        return -1;
        -    }
        -    
        -    if (huge_stack_test() < 0) {
        -        srs_trace("huge_stack_test failed");
        -        return -1;
        -    }
        -    
        -    if (thread_test() < 0) {
        -        srs_trace("thread_test failed");
        -        return -1;
        -    }
        -    
        -    if (sync_test() < 0) {
        -        srs_trace("sync_test failed");
        -        return -1;
        -    }
        -    
        -    if (io_test() < 0) {
        -        srs_trace("io_test failed");
        -        return -1;
        -    }
        -    
        -    if (pipe_test() < 0) {
        -        srs_trace("pipe_test failed");
        -        return -1;
        -    }
        -    
        -    // cleanup.
        -    srs_trace("wait for all thread completed");
        -    st_thread_exit(NULL);
        -    // the following never enter, 
        -    // the above code will exit when all thread exit,
        -    // current is a primordial st-thread, when all thread exit,
        -    // the st idle thread will exit(0), see _st_idle_thread_start()
        -    srs_trace("all thread completed");
        -    
        -    return 0;
        -}
        -
        diff --git a/trunk/research/st/st/init b/trunk/research/st/st/init
        deleted file mode 100644
        index 61604b75f..000000000
        --- a/trunk/research/st/st/init
        +++ /dev/null
        @@ -1,3 +0,0 @@
        -#ifndef _st_icpp_init_stub
        -#define _st_icpp_init_stub
        -#endif
        diff --git a/trunk/research/st/st/st.upp b/trunk/research/st/st/st.upp
        deleted file mode 100755
        index dab6d4958..000000000
        --- a/trunk/research/st/st/st.upp
        +++ /dev/null
        @@ -1,18 +0,0 @@
        -file
        -	main readonly separator,
        -	..\srs.c,
        -	st readonly separator,
        -	..\common.h,
        -	..\event.c,
        -	..\io.c,
        -	..\key.c,
        -	..\md.h,
        -	..\md.S,
        -	..\public.h,
        -	..\sched.c,
        -	..\stk.c,
        -	..\sync.c;
        -
        -mainconfig
        -	"" = "MAIN";
        -
        diff --git a/trunk/research/st/stk.c b/trunk/research/st/stk.c
        deleted file mode 100644
        index c26223ba5..000000000
        --- a/trunk/research/st/stk.c
        +++ /dev/null
        @@ -1,169 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -/*
        - * This file is derived directly from Netscape Communications Corporation,
        - * and consists of extensive modifications made during the year(s) 1999-2000.
        - */
        -
        -#include 
        -#include 
        -#include 
        -#include 
        -#include 
        -#include "common.h"
        -
        -/* How much space to leave between the stacks, at each end */
        -#define REDZONE    _ST_PAGE_SIZE
        -
        -_st_clist_t _st_free_stacks = ST_INIT_STATIC_CLIST(&_st_free_stacks);
        -int _st_num_free_stacks = 0;
        -int _st_randomize_stacks = 0;
        -
        -static char *_st_new_stk_segment(int size);
        -
        -/**
        -The below comments is by winlin:
        -The stack memory struct:
        -    | REDZONE |          stack         |  extra  | REDZONE |
        -    +---------+------------------------+---------+---------+
        -    |    4k   |                        |   4k/0  |    4k   |
        -    +---------+------------------------+---------+---------+
        -    vaddr     bottom                   top
        -When _st_randomize_stacks is on, by st_randomize_stacks(),
        -the bottom and top will random movided in the extra:
        -        long offset = (random() % extra) & ~0xf;
        -        ts->stk_bottom += offset;
        -        ts->stk_top += offset;
        -Both REDZONE are protected by mprotect when DEBUG is on.
        -*/
        -_st_stack_t *_st_stack_new(int stack_size)
        -{
        -    _st_clist_t *qp;
        -    _st_stack_t *ts;
        -    int extra;
        -    
        -    // TODO: WINLIN: remove the stack reuse.
        -    for (qp = _st_free_stacks.next; qp != &_st_free_stacks; qp = qp->next) {
        -        ts = _ST_THREAD_STACK_PTR(qp);
        -        if (ts->stk_size >= stack_size) {
        -            /* Found a stack that is big enough */
        -            ST_REMOVE_LINK(&ts->links);
        -            _st_num_free_stacks--;
        -            ts->links.next = NULL;
        -            ts->links.prev = NULL;
        -            return ts;
        -        }
        -    }
        -    
        -    /* Make a new thread stack object. */
        -    if ((ts = (_st_stack_t *)calloc(1, sizeof(_st_stack_t))) == NULL) {
        -        return NULL;
        -    }
        -    extra = _st_randomize_stacks ? _ST_PAGE_SIZE : 0;
        -    ts->vaddr_size = stack_size + 2*REDZONE + extra;
        -    ts->vaddr = _st_new_stk_segment(ts->vaddr_size);
        -    if (!ts->vaddr) {
        -        free(ts);
        -        return NULL;
        -    }
        -    ts->stk_size = stack_size;
        -    ts->stk_bottom = ts->vaddr + REDZONE;
        -    ts->stk_top = ts->stk_bottom + stack_size;
        -    
        -#ifdef DEBUG
        -    mprotect(ts->vaddr, REDZONE, PROT_NONE);
        -    mprotect(ts->stk_top + extra, REDZONE, PROT_NONE);
        -#endif
        -    
        -    if (extra) {
        -        long offset = (random() % extra) & ~0xf;
        -        
        -        ts->stk_bottom += offset;
        -        ts->stk_top += offset;
        -    }
        -    
        -    return ts;
        -}
        -
        -/*
        - * Free the stack for the current thread
        - */
        -void _st_stack_free(_st_stack_t *ts)
        -{
        -    if (!ts) {
        -        return;
        -    }
        -    
        -    /* Put the stack on the free list */
        -    ST_APPEND_LINK(&ts->links, _st_free_stacks.prev);
        -    _st_num_free_stacks++;
        -}
        -
        -static char *_st_new_stk_segment(int size)
        -{
        -#ifdef MALLOC_STACK
        -    void *vaddr = malloc(size);
        -#else
        -    #error "Only Supports Malloc Stack"
        -#endif
        -    
        -    return (char *)vaddr;
        -}
        -
        -/* Not used */
        -#if 0
        -void _st_delete_stk_segment(char *vaddr, int size)
        -{
        -#ifdef MALLOC_STACK
        -    free(vaddr);
        -#else
        -    #error Unknown Stack Malloc
        -#endif
        -}
        -#endif
        -
        -int st_randomize_stacks(int on)
        -{
        -    int wason = _st_randomize_stacks;
        -    
        -    _st_randomize_stacks = on;
        -    if (on) {
        -        srandom((unsigned int) st_utime());
        -    }
        -    
        -    return wason;
        -}
        diff --git a/trunk/research/st/sync.c b/trunk/research/st/sync.c
        deleted file mode 100644
        index 3e5324084..000000000
        --- a/trunk/research/st/sync.c
        +++ /dev/null
        @@ -1,352 +0,0 @@
        -/* 
        - * The contents of this file are subject to the Mozilla Public
        - * License Version 1.1 (the "License"); you may not use this file
        - * except in compliance with the License. You may obtain a copy of
        - * the License at http://www.mozilla.org/MPL/
        - * 
        - * Software distributed under the License is distributed on an "AS
        - * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
        - * implied. See the License for the specific language governing
        - * rights and limitations under the License.
        - * 
        - * The Original Code is the Netscape Portable Runtime library.
        - * 
        - * The Initial Developer of the Original Code is Netscape
        - * Communications Corporation.  Portions created by Netscape are 
        - * Copyright (C) 1994-2000 Netscape Communications Corporation.  All
        - * Rights Reserved.
        - * 
        - * Contributor(s):  Silicon Graphics, Inc.
        - * 
        - * Portions created by SGI are Copyright (C) 2000-2001 Silicon
        - * Graphics, Inc.  All Rights Reserved.
        - * 
        - * Alternatively, the contents of this file may be used under the
        - * terms of the GNU General Public License Version 2 or later (the
        - * "GPL"), in which case the provisions of the GPL are applicable 
        - * instead of those above.  If you wish to allow use of your 
        - * version of this file only under the terms of the GPL and not to
        - * allow others to use your version of this file under the MPL,
        - * indicate your decision by deleting the provisions above and
        - * replace them with the notice and other provisions required by
        - * the GPL.  If you do not delete the provisions above, a recipient
        - * may use your version of this file under either the MPL or the
        - * GPL.
        - */
        -
        -/*
        - * This file is derived directly from Netscape Communications Corporation,
        - * and consists of extensive modifications made during the year(s) 1999-2000.
        - */
        -
        -#include 
        -#include 
        -#include 
        -#include "common.h"
        -
        -extern time_t _st_curr_time;
        -extern st_utime_t _st_last_tset;
        -extern int _st_active_count;
        -
        -static st_utime_t (*_st_utime)(void) = NULL;
        -
        -/*****************************************
        - * Time functions
        - */
        -
        -st_utime_t st_utime(void)
        -{
        -    if (_st_utime == NULL) {
        -#ifdef MD_GET_UTIME
        -        MD_GET_UTIME();
        -#else
        -        #error Unknown OS
        -#endif
        -    }
        -    
        -    return (*_st_utime)();
        -}
        -
        -int st_set_utime_function(st_utime_t (*func)(void))
        -{
        -    if (_st_active_count) {
        -        errno = EINVAL;
        -        return -1;
        -    }
        -    
        -    _st_utime = func;
        -    
        -    return 0;
        -}
        -
        -st_utime_t st_utime_last_clock(void)
        -{
        -    return _ST_LAST_CLOCK;
        -}
        -
        -int st_timecache_set(int on)
        -{
        -    int wason = (_st_curr_time) ? 1 : 0;
        -    
        -    if (on) {
        -        _st_curr_time = time(NULL);
        -        _st_last_tset = st_utime();
        -    } else {
        -        _st_curr_time = 0;
        -    }
        -    
        -    return wason;
        -}
        -
        -time_t st_time(void)
        -{
        -    if (_st_curr_time) {
        -        return _st_curr_time;
        -    }
        -    
        -    return time(NULL);
        -}
        -
        -int st_usleep(st_utime_t usecs)
        -{
        -    _st_thread_t *me = _ST_CURRENT_THREAD();
        -    
        -    if (me->flags & _ST_FL_INTERRUPT) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        return -1;
        -    }
        -    
        -    if (usecs != ST_UTIME_NO_TIMEOUT) {
        -        me->state = _ST_ST_SLEEPING;
        -        _ST_ADD_SLEEPQ(me, usecs);
        -    } else {
        -        me->state = _ST_ST_SUSPENDED;
        -    }
        -    
        -    _ST_SWITCH_CONTEXT(me);
        -    
        -    if (me->flags & _ST_FL_INTERRUPT) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        return -1;
        -    }
        -    
        -    return 0;
        -}
        -
        -int st_sleep(int secs)
        -{
        -    return st_usleep((secs >= 0) ? secs * (st_utime_t) 1000000LL : ST_UTIME_NO_TIMEOUT);
        -}
        -
        -/*****************************************
        - * Condition variable functions
        - */
        -_st_cond_t *st_cond_new(void)
        -{
        -    _st_cond_t *cvar;
        -    
        -    cvar = (_st_cond_t *) calloc(1, sizeof(_st_cond_t));
        -    if (cvar) {
        -        ST_INIT_CLIST(&cvar->wait_q);
        -    }
        -    
        -    return cvar;
        -}
        -
        -int st_cond_destroy(_st_cond_t *cvar)
        -{
        -    if (cvar->wait_q.next != &cvar->wait_q) {
        -        errno = EBUSY;
        -        return -1;
        -    }
        -    
        -    free(cvar);
        -    
        -    return 0;
        -}
        -
        -int st_cond_timedwait(_st_cond_t *cvar, st_utime_t timeout)
        -{
        -    _st_thread_t *me = _ST_CURRENT_THREAD();
        -    int rv;
        -    
        -    if (me->flags & _ST_FL_INTERRUPT) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        return -1;
        -    }
        -    
        -    /* Put caller thread on the condition variable's wait queue */
        -    me->state = _ST_ST_COND_WAIT;
        -    ST_APPEND_LINK(&me->wait_links, &cvar->wait_q);
        -    
        -    if (timeout != ST_UTIME_NO_TIMEOUT) {
        -        _ST_ADD_SLEEPQ(me, timeout);
        -    }
        -    
        -    _ST_SWITCH_CONTEXT(me);
        -    
        -    ST_REMOVE_LINK(&me->wait_links);
        -    rv = 0;
        -    
        -    if (me->flags & _ST_FL_TIMEDOUT) {
        -        me->flags &= ~_ST_FL_TIMEDOUT;
        -        errno = ETIME;
        -        rv = -1;
        -    }
        -    if (me->flags & _ST_FL_INTERRUPT) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        rv = -1;
        -    }
        -    
        -    return rv;
        -}
        -
        -int st_cond_wait(_st_cond_t *cvar)
        -{
        -    return st_cond_timedwait(cvar, ST_UTIME_NO_TIMEOUT);
        -}
        -
        -static int _st_cond_signal(_st_cond_t *cvar, int broadcast)
        -{
        -    _st_thread_t *thread;
        -    _st_clist_t *q;
        -    
        -    for (q = cvar->wait_q.next; q != &cvar->wait_q; q = q->next) {
        -        thread = _ST_THREAD_WAITQ_PTR(q);
        -        if (thread->state == _ST_ST_COND_WAIT) {
        -            if (thread->flags & _ST_FL_ON_SLEEPQ) {
        -                _ST_DEL_SLEEPQ(thread);
        -            }
        -            
        -            /* Make thread runnable */
        -            thread->state = _ST_ST_RUNNABLE;
        -            _ST_ADD_RUNQ(thread);
        -            if (!broadcast) {
        -                break;
        -            }
        -        }
        -    }
        -    
        -    return 0;
        -}
        -
        -int st_cond_signal(_st_cond_t *cvar)
        -{
        -    return _st_cond_signal(cvar, 0);
        -}
        -
        -int st_cond_broadcast(_st_cond_t *cvar)
        -{
        -    return _st_cond_signal(cvar, 1);
        -}
        -
        -/*****************************************
        - * Mutex functions
        - */
        -_st_mutex_t *st_mutex_new(void)
        -{
        -    _st_mutex_t *lock;
        -    
        -    lock = (_st_mutex_t *) calloc(1, sizeof(_st_mutex_t));
        -    if (lock) {
        -        ST_INIT_CLIST(&lock->wait_q);
        -        lock->owner = NULL;
        -    }
        -    
        -    return lock;
        -}
        -
        -int st_mutex_destroy(_st_mutex_t *lock)
        -{
        -    if (lock->owner != NULL || lock->wait_q.next != &lock->wait_q) {
        -        errno = EBUSY;
        -        return -1;
        -    }
        -    
        -    free(lock);
        -    
        -    return 0;
        -}
        -
        -int st_mutex_lock(_st_mutex_t *lock)
        -{
        -    _st_thread_t *me = _ST_CURRENT_THREAD();
        -    
        -    if (me->flags & _ST_FL_INTERRUPT) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        return -1;
        -    }
        -    
        -    if (lock->owner == NULL) {
        -        /* Got the mutex */
        -        lock->owner = me;
        -        return 0;
        -    }
        -    
        -    if (lock->owner == me) {
        -        errno = EDEADLK;
        -        return -1;
        -    }
        -    
        -    /* Put caller thread on the mutex's wait queue */
        -    me->state = _ST_ST_LOCK_WAIT;
        -    ST_APPEND_LINK(&me->wait_links, &lock->wait_q);
        -    
        -    _ST_SWITCH_CONTEXT(me);
        -    
        -    ST_REMOVE_LINK(&me->wait_links);
        -    
        -    if ((me->flags & _ST_FL_INTERRUPT) && lock->owner != me) {
        -        me->flags &= ~_ST_FL_INTERRUPT;
        -        errno = EINTR;
        -        return -1;
        -    }
        -    
        -    return 0;
        -}
        -
        -int st_mutex_unlock(_st_mutex_t *lock)
        -{
        -    _st_thread_t *thread;
        -    _st_clist_t *q;
        -    
        -    if (lock->owner != _ST_CURRENT_THREAD()) {
        -        errno = EPERM;
        -        return -1;
        -    }
        -    
        -    for (q = lock->wait_q.next; q != &lock->wait_q; q = q->next) {
        -        thread = _ST_THREAD_WAITQ_PTR(q);
        -        if (thread->state == _ST_ST_LOCK_WAIT) {
        -            lock->owner = thread;
        -            /* Make thread runnable */
        -            thread->state = _ST_ST_RUNNABLE;
        -            _ST_ADD_RUNQ(thread);
        -            return 0;
        -        }
        -    }
        -    
        -    /* No threads waiting on this mutex */
        -    lock->owner = NULL;
        -    
        -    return 0;
        -}
        -
        -int st_mutex_trylock(_st_mutex_t *lock)
        -{
        -    if (lock->owner != NULL) {
        -        errno = EBUSY;
        -        return -1;
        -    }
        -    
        -    /* Got the mutex */
        -    lock->owner = _ST_CURRENT_THREAD();
        -    
        -    return 0;
        -}
        -
        
        From c362bfc3abe0413fc1097ad8f6ebb4e0b1af4328 Mon Sep 17 00:00:00 2001
        From: winlin 
        Date: Sun, 19 Apr 2020 21:58:39 +0800
        Subject: [PATCH 18/35] Add msg zerocopy research code
        
        ---
         trunk/research/msg_zerocopy/.gitignore |  2 +
         trunk/research/msg_zerocopy/Makefile   |  8 +++
         trunk/research/msg_zerocopy/client.cpp | 61 ++++++++++++++++++++
         trunk/research/msg_zerocopy/server.cpp | 79 ++++++++++++++++++++++++++
         4 files changed, 150 insertions(+)
         create mode 100644 trunk/research/msg_zerocopy/.gitignore
         create mode 100644 trunk/research/msg_zerocopy/Makefile
         create mode 100644 trunk/research/msg_zerocopy/client.cpp
         create mode 100644 trunk/research/msg_zerocopy/server.cpp
        
        diff --git a/trunk/research/msg_zerocopy/.gitignore b/trunk/research/msg_zerocopy/.gitignore
        new file mode 100644
        index 000000000..3ddc9a018
        --- /dev/null
        +++ b/trunk/research/msg_zerocopy/.gitignore
        @@ -0,0 +1,2 @@
        +server
        +client
        \ No newline at end of file
        diff --git a/trunk/research/msg_zerocopy/Makefile b/trunk/research/msg_zerocopy/Makefile
        new file mode 100644
        index 000000000..628939550
        --- /dev/null
        +++ b/trunk/research/msg_zerocopy/Makefile
        @@ -0,0 +1,8 @@
        +
        +default: server client
        +
        +server: server.cpp ../../objs/st/libst.a
        +	gcc -g -O0 -I../../objs/st/ $^ -o $@
        +
        +client: client.cpp ../../objs/st/libst.a
        +	gcc -g -O0 -I../../objs/st/ $^ -o $@
        diff --git a/trunk/research/msg_zerocopy/client.cpp b/trunk/research/msg_zerocopy/client.cpp
        new file mode 100644
        index 000000000..c30f3584d
        --- /dev/null
        +++ b/trunk/research/msg_zerocopy/client.cpp
        @@ -0,0 +1,61 @@
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +
        +int main(int argc, char** argv)
        +{
        +    if (argc < 3) {
        +        printf("Usage: %s  \n", argv[0]);
        +        printf("For example:\n");
        +        printf("        %s 127.0.0.1 8000\n", argv[0]);
        +        exit(-1);
        +    }
        +
        +    assert(!st_set_eventsys(ST_EVENTSYS_ALT));
        +    assert(!st_init());
        +
        +    int fd = socket(PF_INET, SOCK_DGRAM, 0);
        +    assert(fd > 0);
        +
        +    st_netfd_t stfd = st_netfd_open_socket(fd);
        +    assert(stfd);
        +
        +    sockaddr_in peer;
        +    memset(&peer, 0, sizeof(sockaddr_in));
        +
        +    int port = 8000;
        +    const char* host = "127.0.0.1";
        +    peer.sin_family = AF_INET;
        +    peer.sin_port = htons(port);
        +    peer.sin_addr.s_addr = inet_addr(host);
        +
        +    char buf[1500];
        +    memset(buf, 0, sizeof(buf));
        +    memcpy(buf, "Hello", 5);
        +
        +    iovec iov;
        +    iov.iov_base = buf;
        +    iov.iov_len = strlen(buf);
        +
        +    msghdr msg;
        +    memset(&msg, 0, sizeof(msghdr));
        +    msg.msg_name = (sockaddr_in*)&peer;
        +    msg.msg_namelen = sizeof(sockaddr_in);
        +    msg.msg_iov = &iov;
        +    msg.msg_iovlen = 1;
        +
        +    int r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +    printf("Ping %s:%d %d bytes, r0=%d, %s\n", host, port, iov.iov_len, r0, msg.msg_iov->iov_base);
        +
        +    r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +    assert(r0 > 0);
        +    printf("From %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +        msg.msg_flags, msg.msg_iov->iov_base);
        +
        +    return 0;
        +}
        diff --git a/trunk/research/msg_zerocopy/server.cpp b/trunk/research/msg_zerocopy/server.cpp
        new file mode 100644
        index 000000000..cc1259680
        --- /dev/null
        +++ b/trunk/research/msg_zerocopy/server.cpp
        @@ -0,0 +1,79 @@
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +#include 
        +
        +int main(int argc, char** argv)
        +{
        +    if (argc < 3) {
        +        printf("Usage: %s  \n", argv[0]);
        +        printf("For example:\n");
        +        printf("        %s 0.0.0.0 8000\n", argv[0]);
        +        exit(-1);
        +    }
        +
        +    assert(!st_set_eventsys(ST_EVENTSYS_ALT));
        +    assert(!st_init());
        +
        +    int fd = socket(PF_INET, SOCK_DGRAM, 0);
        +    assert(fd > 0);
        +
        +    int n = 1;
        +    int r0 = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, (char *)&n, sizeof(n));
        +    assert(!r0);
        +
        +    sockaddr_in addr;
        +    memset(&addr, 0, sizeof(sockaddr_in));
        +
        +    int port = 8000;
        +    addr.sin_family = AF_INET;
        +    addr.sin_port = htons(port);
        +    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
        +
        +    r0 = bind(fd, (sockaddr *)&addr, sizeof(sockaddr_in));
        +    assert(!r0);
        +
        +    st_netfd_t stfd = st_netfd_open_socket(fd);
        +    assert(stfd);
        +
        +    printf("Listen at udp://%d\n", port);
        +
        +    msghdr msg;
        +    memset(&msg, 0, sizeof(msghdr));
        +
        +    sockaddr_in peer;
        +    memset(&peer, 0, sizeof(sockaddr_in));
        +    msg.msg_name = (sockaddr_in*)&peer;
        +    msg.msg_namelen = sizeof(sockaddr_in);
        +
        +    char buf[1500];
        +    memset(buf, 0, sizeof(buf));
        +
        +    iovec iov;
        +    memset(&iov, 0, sizeof(iovec));
        +    iov.iov_base = buf;
        +    iov.iov_len = sizeof(buf);
        +    msg.msg_iov = &iov;
        +    msg.msg_iovlen = 1;
        +
        +    while (true) {
        +        r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +        assert(r0 > 0);
        +        printf("From %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +            msg.msg_flags, msg.msg_iov->iov_base);
        +
        +        memcpy(msg.msg_iov->iov_base, "World", 5);
        +        msg.msg_iov->iov_len = 5;
        +
        +        r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +        assert(r0 > 0);
        +        printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +            msg.msg_flags, msg.msg_iov->iov_base);
        +    }
        +
        +    return 0;
        +}
        
        From 2e9a561acf91eea99a6b2db7b92397bf97d991a6 Mon Sep 17 00:00:00 2001
        From: winlin 
        Date: Sun, 19 Apr 2020 22:16:54 +0800
        Subject: [PATCH 19/35] Update research zerocopy
        
        ---
         trunk/research/msg_zerocopy/client.cpp | 6 +++---
         trunk/research/msg_zerocopy/server.cpp | 7 ++++---
         2 files changed, 7 insertions(+), 6 deletions(-)
        
        diff --git a/trunk/research/msg_zerocopy/client.cpp b/trunk/research/msg_zerocopy/client.cpp
        index c30f3584d..d3b501171 100644
        --- a/trunk/research/msg_zerocopy/client.cpp
        +++ b/trunk/research/msg_zerocopy/client.cpp
        @@ -28,8 +28,8 @@ int main(int argc, char** argv)
             sockaddr_in peer;
             memset(&peer, 0, sizeof(sockaddr_in));
         
        -    int port = 8000;
        -    const char* host = "127.0.0.1";
        +    int port = atoi(argv[2]);
        +    char* host = argv[1];
             peer.sin_family = AF_INET;
             peer.sin_port = htons(port);
             peer.sin_addr.s_addr = inet_addr(host);
        @@ -54,7 +54,7 @@ int main(int argc, char** argv)
         
             r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
             assert(r0 > 0);
        -    printf("From %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +    printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
                 msg.msg_flags, msg.msg_iov->iov_base);
         
             return 0;
        diff --git a/trunk/research/msg_zerocopy/server.cpp b/trunk/research/msg_zerocopy/server.cpp
        index cc1259680..0b1145fa3 100644
        --- a/trunk/research/msg_zerocopy/server.cpp
        +++ b/trunk/research/msg_zerocopy/server.cpp
        @@ -29,10 +29,11 @@ int main(int argc, char** argv)
             sockaddr_in addr;
             memset(&addr, 0, sizeof(sockaddr_in));
         
        -    int port = 8000;
        +    int port = atoi(argv[2]);
        +    char* host = argv[1];
             addr.sin_family = AF_INET;
             addr.sin_port = htons(port);
        -    addr.sin_addr.s_addr = inet_addr("0.0.0.0");
        +    addr.sin_addr.s_addr = inet_addr(host);
         
             r0 = bind(fd, (sockaddr *)&addr, sizeof(sockaddr_in));
             assert(!r0);
        @@ -40,7 +41,7 @@ int main(int argc, char** argv)
             st_netfd_t stfd = st_netfd_open_socket(fd);
             assert(stfd);
         
        -    printf("Listen at udp://%d\n", port);
        +    printf("Listen at udp://%s:%d\n", host, port);
         
             msghdr msg;
             memset(&msg, 0, sizeof(msghdr));
        
        From 8f9cc38f68c4877559609c527e8847fdb220862b Mon Sep 17 00:00:00 2001
        From: winlin 
        Date: Mon, 20 Apr 2020 08:02:53 +0800
        Subject: [PATCH 20/35] Refine zero copy research
        
        ---
         trunk/research/msg_zerocopy/client.cpp | 23 ++++++++++++++---------
         trunk/research/msg_zerocopy/server.cpp | 23 ++++++++++++++---------
         2 files changed, 28 insertions(+), 18 deletions(-)
        
        diff --git a/trunk/research/msg_zerocopy/client.cpp b/trunk/research/msg_zerocopy/client.cpp
        index d3b501171..a4126145d 100644
        --- a/trunk/research/msg_zerocopy/client.cpp
        +++ b/trunk/research/msg_zerocopy/client.cpp
        @@ -9,13 +9,18 @@
         
         int main(int argc, char** argv)
         {
        -    if (argc < 3) {
        -        printf("Usage: %s  \n", argv[0]);
        +    if (argc < 4) {
        +        printf("Usage: %s   \n", argv[0]);
                 printf("For example:\n");
        -        printf("        %s 127.0.0.1 8000\n", argv[0]);
        +        printf("        %s 127.0.0.1 8000 true\n", argv[0]);
                 exit(-1);
             }
         
        +    int port = atoi(argv[2]);
        +    char* host = argv[1];
        +    bool pong = !strcmp(argv[3], "pong");
        +    printf("Server listen %s:%d, pong %d\n", host, port, pong);
        +
             assert(!st_set_eventsys(ST_EVENTSYS_ALT));
             assert(!st_init());
         
        @@ -28,8 +33,6 @@ int main(int argc, char** argv)
             sockaddr_in peer;
             memset(&peer, 0, sizeof(sockaddr_in));
         
        -    int port = atoi(argv[2]);
        -    char* host = argv[1];
             peer.sin_family = AF_INET;
             peer.sin_port = htons(port);
             peer.sin_addr.s_addr = inet_addr(host);
        @@ -52,10 +55,12 @@ int main(int argc, char** argv)
             int r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
             printf("Ping %s:%d %d bytes, r0=%d, %s\n", host, port, iov.iov_len, r0, msg.msg_iov->iov_base);
         
        -    r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        -    assert(r0 > 0);
        -    printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        -        msg.msg_flags, msg.msg_iov->iov_base);
        +    if (pong) {
        +        r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +        assert(r0 > 0);
        +        printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +            msg.msg_flags, msg.msg_iov->iov_base);
        +    }
         
             return 0;
         }
        diff --git a/trunk/research/msg_zerocopy/server.cpp b/trunk/research/msg_zerocopy/server.cpp
        index 0b1145fa3..57978c4be 100644
        --- a/trunk/research/msg_zerocopy/server.cpp
        +++ b/trunk/research/msg_zerocopy/server.cpp
        @@ -9,13 +9,18 @@
         
         int main(int argc, char** argv)
         {
        -    if (argc < 3) {
        -        printf("Usage: %s  \n", argv[0]);
        +    if (argc < 4) {
        +        printf("Usage: %s   \n", argv[0]);
                 printf("For example:\n");
        -        printf("        %s 0.0.0.0 8000\n", argv[0]);
        +        printf("        %s 0.0.0.0 8000 true\n", argv[0]);
                 exit(-1);
             }
         
        +    int port = atoi(argv[2]);
        +    char* host = argv[1];
        +    bool pong = !strcmp(argv[3], "pong");
        +    printf("Server listen %s:%d, pong %d\n", host, port, pong);
        +
             assert(!st_set_eventsys(ST_EVENTSYS_ALT));
             assert(!st_init());
         
        @@ -29,8 +34,6 @@ int main(int argc, char** argv)
             sockaddr_in addr;
             memset(&addr, 0, sizeof(sockaddr_in));
         
        -    int port = atoi(argv[2]);
        -    char* host = argv[1];
             addr.sin_family = AF_INET;
             addr.sin_port = htons(port);
             addr.sin_addr.s_addr = inet_addr(host);
        @@ -70,10 +73,12 @@ int main(int argc, char** argv)
                 memcpy(msg.msg_iov->iov_base, "World", 5);
                 msg.msg_iov->iov_len = 5;
         
        -        r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        -        assert(r0 > 0);
        -        printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        -            msg.msg_flags, msg.msg_iov->iov_base);
        +        if (pong) {
        +            r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +            assert(r0 > 0);
        +            printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +                msg.msg_flags, msg.msg_iov->iov_base);
        +        }
             }
         
             return 0;
        
        From a3de167bc7aafee6a721e43b8c1609d3eda034b8 Mon Sep 17 00:00:00 2001
        From: winlin 
        Date: Mon, 20 Apr 2020 08:42:53 +0800
        Subject: [PATCH 21/35] For zerocopy research, support delay in server
        
        ---
         trunk/research/msg_zerocopy/Makefile   |  8 ++++++++
         trunk/research/msg_zerocopy/client.cpp |  4 ++--
         trunk/research/msg_zerocopy/server.cpp | 19 +++++++++++++------
         3 files changed, 23 insertions(+), 8 deletions(-)
        
        diff --git a/trunk/research/msg_zerocopy/Makefile b/trunk/research/msg_zerocopy/Makefile
        index 628939550..cd67c1885 100644
        --- a/trunk/research/msg_zerocopy/Makefile
        +++ b/trunk/research/msg_zerocopy/Makefile
        @@ -1,4 +1,6 @@
         
        +.PHONY: default clean
        +
         default: server client
         
         server: server.cpp ../../objs/st/libst.a
        @@ -6,3 +8,9 @@ server: server.cpp ../../objs/st/libst.a
         
         client: client.cpp ../../objs/st/libst.a
         	gcc -g -O0 -I../../objs/st/ $^ -o $@
        +
        +../../objs/st/libst.a: ../../Makefile
        +	(cd ../../ && $(MAKE) st)
        +
        +clean:
        +	rm -f server client ../../objs/st/libst.a
        diff --git a/trunk/research/msg_zerocopy/client.cpp b/trunk/research/msg_zerocopy/client.cpp
        index a4126145d..acae19459 100644
        --- a/trunk/research/msg_zerocopy/client.cpp
        +++ b/trunk/research/msg_zerocopy/client.cpp
        @@ -16,9 +16,9 @@ int main(int argc, char** argv)
                 exit(-1);
             }
         
        -    int port = atoi(argv[2]);
             char* host = argv[1];
        -    bool pong = !strcmp(argv[3], "pong");
        +    int port = atoi(argv[2]);
        +    bool pong = !strcmp(argv[3], "true");
             printf("Server listen %s:%d, pong %d\n", host, port, pong);
         
             assert(!st_set_eventsys(ST_EVENTSYS_ALT));
        diff --git a/trunk/research/msg_zerocopy/server.cpp b/trunk/research/msg_zerocopy/server.cpp
        index 57978c4be..417e8c529 100644
        --- a/trunk/research/msg_zerocopy/server.cpp
        +++ b/trunk/research/msg_zerocopy/server.cpp
        @@ -9,17 +9,20 @@
         
         int main(int argc, char** argv)
         {
        -    if (argc < 4) {
        -        printf("Usage: %s   \n", argv[0]);
        +    if (argc < 5) {
        +        printf("Usage: %s    \n", argv[0]);
        +        printf("    pong        Whether response pong, true|false\n");
        +        printf("    delay       The delay in ms to response pong.\n");
                 printf("For example:\n");
        -        printf("        %s 0.0.0.0 8000 true\n", argv[0]);
        +        printf("        %s 0.0.0.0 8000 true 100\n", argv[0]);
                 exit(-1);
             }
         
        -    int port = atoi(argv[2]);
             char* host = argv[1];
        -    bool pong = !strcmp(argv[3], "pong");
        -    printf("Server listen %s:%d, pong %d\n", host, port, pong);
        +    int port = atoi(argv[2]);
        +    bool pong = !strcmp(argv[3], "true");
        +    int delay = ::atoi(argv[4]);
        +    printf("Server listen %s:%d, pong %d, delay: %dms\n", host, port, pong, delay);
         
             assert(!st_set_eventsys(ST_EVENTSYS_ALT));
             assert(!st_init());
        @@ -74,6 +77,10 @@ int main(int argc, char** argv)
                 msg.msg_iov->iov_len = 5;
         
                 if (pong) {
        +            if (delay > 0) {
        +                st_usleep(delay * 1000);
        +            }
        +
                     r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
                     assert(r0 > 0);
                     printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        
        From 72322836c6368c42c4f285dd23e010993076106d Mon Sep 17 00:00:00 2001
        From: winlin 
        Date: Mon, 20 Apr 2020 20:25:23 +0800
        Subject: [PATCH 22/35] Update demo for zerocopy
        
        ---
         trunk/auto/depends.sh                  |   6 +-
         trunk/research/msg_zerocopy/Makefile   |   4 +-
         trunk/research/msg_zerocopy/client.cpp | 125 +++++++++++++++++++++++--
         trunk/research/msg_zerocopy/server.cpp |  59 +++++++++---
         trunk/src/app/srs_app_rtc_conn.cpp     |   2 +
         5 files changed, 171 insertions(+), 25 deletions(-)
        
        diff --git a/trunk/auto/depends.sh b/trunk/auto/depends.sh
        index 557de4bb3..932c55ade 100755
        --- a/trunk/auto/depends.sh
        +++ b/trunk/auto/depends.sh
        @@ -435,6 +435,10 @@ fi
         # cherrypy for http hooks callback, CherryPy-3.2.4
         #####################################################################################
         if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
        +    # Detect python or python2
        +    python --version >/dev/null 2>&1 && SYS_PYTHON=python;
        +    python2 --version >/dev/null 2>&1 && SYS_PYTHON=python2;
        +    # Install cherrypy for api server.
             if [[ -f ${SRS_OBJS}/${SRS_PLATFORM}/CherryPy-3.2.4/setup.py ]]; then
                 echo "CherryPy-3.2.4 is ok.";
             else
        @@ -442,7 +446,7 @@ if [ $SRS_EXPORT_LIBRTMP_PROJECT = NO ]; then
                 (
                     rm -rf ${SRS_OBJS}/CherryPy-3.2.4 && cd ${SRS_OBJS}/${SRS_PLATFORM} &&
                     unzip -q ../../3rdparty/CherryPy-3.2.4.zip && cd CherryPy-3.2.4 &&
        -            python setup.py install --user --prefix=''
        +            $SYS_PYTHON setup.py install --user --prefix=''
                 )
             fi
             # check status
        diff --git a/trunk/research/msg_zerocopy/Makefile b/trunk/research/msg_zerocopy/Makefile
        index cd67c1885..9cadc3e96 100644
        --- a/trunk/research/msg_zerocopy/Makefile
        +++ b/trunk/research/msg_zerocopy/Makefile
        @@ -4,10 +4,10 @@
         default: server client
         
         server: server.cpp ../../objs/st/libst.a
        -	gcc -g -O0 -I../../objs/st/ $^ -o $@
        +	g++ -g -O0 -I../../objs/st/ $^ -o $@
         
         client: client.cpp ../../objs/st/libst.a
        -	gcc -g -O0 -I../../objs/st/ $^ -o $@
        +	g++ -g -O0 -I../../objs/st/ $^ -o $@
         
         ../../objs/st/libst.a: ../../Makefile
         	(cd ../../ && $(MAKE) st)
        diff --git a/trunk/research/msg_zerocopy/client.cpp b/trunk/research/msg_zerocopy/client.cpp
        index acae19459..3d706bbc1 100644
        --- a/trunk/research/msg_zerocopy/client.cpp
        +++ b/trunk/research/msg_zerocopy/client.cpp
        @@ -7,19 +7,72 @@
         #include 
         #include 
         
        +#include 
        +
        +// @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
        +#include 
        +
        +// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
        +#include 
        +#ifndef SO_EE_ORIGIN_ZEROCOPY
        +#define SO_EE_ORIGIN_ZEROCOPY		5
        +#endif
        +
        +#ifndef SO_ZEROCOPY
        +#define SO_ZEROCOPY	60
        +#endif
        +
        +#ifndef SO_EE_CODE_ZEROCOPY_COPIED
        +#define SO_EE_CODE_ZEROCOPY_COPIED	1
        +#endif
        +
        +#ifndef MSG_ZEROCOPY
        +#define MSG_ZEROCOPY	0x4000000
        +#endif
        +
        +void* receiver(void* arg)
        +{
        +    st_netfd_t stfd = (st_netfd_t)arg;
        +
        +    sockaddr_in peer;
        +    memset(&peer, 0, sizeof(sockaddr_in));
        +
        +    char buf[1500];
        +    memset(buf, 0, sizeof(buf));
        +
        +    iovec iov;
        +    iov.iov_base = buf;
        +    iov.iov_len = sizeof(buf);
        +
        +    msghdr msg;
        +    memset(&msg, 0, sizeof(msghdr));
        +    msg.msg_name = (sockaddr_in*)&peer;
        +    msg.msg_namelen = sizeof(sockaddr_in);
        +    msg.msg_iov = &iov;
        +    msg.msg_iovlen = 1;
        +
        +    int r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +    assert(r0 > 0);
        +    printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +        msg.msg_flags, msg.msg_iov->iov_base);
        +
        +    return NULL;
        +}
        +
         int main(int argc, char** argv)
         {
        -    if (argc < 4) {
        -        printf("Usage: %s   \n", argv[0]);
        +    if (argc < 5) {
        +        printf("Usage: %s    \n", argv[0]);
                 printf("For example:\n");
        -        printf("        %s 127.0.0.1 8000 true\n", argv[0]);
        +        printf("        %s 127.0.0.1 8000 true true\n", argv[0]);
                 exit(-1);
             }
         
             char* host = argv[1];
             int port = atoi(argv[2]);
             bool pong = !strcmp(argv[3], "true");
        -    printf("Server listen %s:%d, pong %d\n", host, port, pong);
        +    bool zerocopy = !strcmp(argv[4], "true");
        +    printf("Server listen %s:%d, pong %d, zerocopy %d\n", host, port, pong, zerocopy);
         
             assert(!st_set_eventsys(ST_EVENTSYS_ALT));
             assert(!st_init());
        @@ -27,8 +80,31 @@ int main(int argc, char** argv)
             int fd = socket(PF_INET, SOCK_DGRAM, 0);
             assert(fd > 0);
         
        +    // @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c
        +    if (zerocopy) {
        +        int one = 1;
        +        int r0 = setsockopt(fd, SOL_SOCKET, SO_ZEROCOPY, &one, sizeof(one));
        +// MSG_ZEROCOPY for UDP was added in commit b5947e5d1e71 ("udp: msg_zerocopy") in Linux 5.0.
        +// @see https://lore.kernel.org/netdev/CA+FuTSfBFqRViKfG5crEv8xLMgAkp3cZ+yeuELK5TVv61xT=Yw@mail.gmail.com/
        +#if LINUX_VERSION_CODE < KERNEL_VERSION(5,0,0)
        +        if (r0 == -1) {
        +            printf("MSG_ZEROCOPY should be kernel 5.0+, kernel %#x, errno=%d\n", LINUX_VERSION_CODE, 524);
        +            exit(-1);
        +        }
        +#endif
        +        assert(!r0);
        +
        +        printf("epoll events EPOLLERR=%#x, EPOLLHUP=%#x\n", EPOLLERR, EPOLLHUP);
        +    }
        +
             st_netfd_t stfd = st_netfd_open_socket(fd);
             assert(stfd);
        +    printf("Client fd=%d\n", fd);
        +
        +    if (pong) {
        +        st_thread_t r0 = st_thread_create(receiver, stfd, 0, 0);
        +        assert(r0);
        +    }
         
             sockaddr_in peer;
             memset(&peer, 0, sizeof(sockaddr_in));
        @@ -52,15 +128,44 @@ int main(int argc, char** argv)
             msg.msg_iov = &iov;
             msg.msg_iovlen = 1;
         
        -    int r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +    int r0;
        +    if (zerocopy) {
        +        r0 = st_sendmsg(stfd, &msg, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
        +    } else {
        +        r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +    }
        +    assert(r0 > 0);
             printf("Ping %s:%d %d bytes, r0=%d, %s\n", host, port, iov.iov_len, r0, msg.msg_iov->iov_base);
         
        -    if (pong) {
        -        r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        -        assert(r0 > 0);
        -        printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        -            msg.msg_flags, msg.msg_iov->iov_base);
        +    // Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
        +    // See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393
        +    char control[100];
        +    msg.msg_control = control;
        +    msg.msg_controllen = sizeof(control);
        +    // Note that the r0 is 0, the reception is in the control.
        +    r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT);
        +    assert(r0 >= 0);
        +    assert(msg.msg_flags == MSG_ERRQUEUE);
        +
        +    // Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing
        +    cmsghdr* cm = CMSG_FIRSTHDR(&msg);
        +    assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR);
        +
        +    sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm);
        +    assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY);
        +
        +    uint32_t hi = serr->ee_data;
        +    uint32_t lo = serr->ee_info;
        +    uint32_t range = hi - lo + 1;
        +    printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), %d [%d, %d]\n",
        +        msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi);
        +
        +    // Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies
        +    if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) {
        +        printf("Warning: Defered copies, should stop zerocopy\n");
             }
         
        +    st_sleep(-1);
        +
             return 0;
         }
        diff --git a/trunk/research/msg_zerocopy/server.cpp b/trunk/research/msg_zerocopy/server.cpp
        index 417e8c529..6f83fde97 100644
        --- a/trunk/research/msg_zerocopy/server.cpp
        +++ b/trunk/research/msg_zerocopy/server.cpp
        @@ -7,6 +7,46 @@
         #include 
         #include 
         
        +struct message {
        +    st_netfd_t stfd;
        +    sockaddr_in peer;
        +    int delay;
        +};
        +
        +void* sender(void* arg)
        +{
        +    message* p = (message*)arg;
        +
        +    int delay = p->delay;
        +    if (delay > 0) {
        +        st_usleep(delay * 1000);
        +    }
        +
        +    msghdr msg;
        +    memset(&msg, 0, sizeof(msghdr));
        +
        +    sockaddr_in peer = p->peer;
        +    msg.msg_name = (sockaddr_in*)&peer;
        +    msg.msg_namelen = sizeof(sockaddr_in);
        +
        +    char buf[] = "World";
        +
        +    iovec iov;
        +    memset(&iov, 0, sizeof(iovec));
        +    iov.iov_base = buf;
        +    iov.iov_len = sizeof(buf);
        +    msg.msg_iov = &iov;
        +    msg.msg_iovlen = 1;
        +
        +    st_netfd_t stfd = p->stfd;
        +    int r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +    assert(r0 > 0);
        +    printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +        msg.msg_flags, msg.msg_iov->iov_base);
        +
        +    return NULL;
        +}
        +
         int main(int argc, char** argv)
         {
             if (argc < 5) {
        @@ -47,7 +87,7 @@ int main(int argc, char** argv)
             st_netfd_t stfd = st_netfd_open_socket(fd);
             assert(stfd);
         
        -    printf("Listen at udp://%s:%d\n", host, port);
        +    printf("Listen at udp://%s:%d, fd=%d\n", host, port, fd);
         
             msghdr msg;
             memset(&msg, 0, sizeof(msghdr));
        @@ -73,18 +113,13 @@ int main(int argc, char** argv)
                 printf("From %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
                     msg.msg_flags, msg.msg_iov->iov_base);
         
        -        memcpy(msg.msg_iov->iov_base, "World", 5);
        -        msg.msg_iov->iov_len = 5;
        -
                 if (pong) {
        -            if (delay > 0) {
        -                st_usleep(delay * 1000);
        -            }
        -
        -            r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        -            assert(r0 > 0);
        -            printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        -                msg.msg_flags, msg.msg_iov->iov_base);
        +            message* msg = new message();
        +            msg->stfd = stfd;
        +            msg->peer = peer;
        +            msg->delay = delay;
        +            st_thread_t r0 = st_thread_create(sender, msg, 0, 0);
        +            assert(r0);
                 }
             }
         
        diff --git a/trunk/src/app/srs_app_rtc_conn.cpp b/trunk/src/app/srs_app_rtc_conn.cpp
        index dd9a791b2..e1a95765a 100644
        --- a/trunk/src/app/srs_app_rtc_conn.cpp
        +++ b/trunk/src/app/srs_app_rtc_conn.cpp
        @@ -34,6 +34,8 @@ using namespace std;
         #include 
         
         #include 
        +// Define macro for UDP GSO.
        +// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/udpgso.c
         #ifndef UDP_SEGMENT
         #define UDP_SEGMENT             103
         #endif
        
        From 74416e476aa0f85e137ab0531af25ba9b2c123ac Mon Sep 17 00:00:00 2001
        From: winlin 
        Date: Mon, 20 Apr 2020 21:18:49 +0800
        Subject: [PATCH 23/35] Refine research zerocopy, use batch for reception
        
        ---
         trunk/research/msg_zerocopy/client.cpp | 168 ++++++++++++++++---------
         1 file changed, 107 insertions(+), 61 deletions(-)
        
        diff --git a/trunk/research/msg_zerocopy/client.cpp b/trunk/research/msg_zerocopy/client.cpp
        index 3d706bbc1..cdfb18bc6 100644
        --- a/trunk/research/msg_zerocopy/client.cpp
        +++ b/trunk/research/msg_zerocopy/client.cpp
        @@ -34,37 +34,78 @@ void* receiver(void* arg)
         {
             st_netfd_t stfd = (st_netfd_t)arg;
         
        -    sockaddr_in peer;
        -    memset(&peer, 0, sizeof(sockaddr_in));
        +    for (;;) {
        +        sockaddr_in peer;
        +        memset(&peer, 0, sizeof(sockaddr_in));
         
        -    char buf[1500];
        -    memset(buf, 0, sizeof(buf));
        +        char buf[1500];
        +        memset(buf, 0, sizeof(buf));
         
        -    iovec iov;
        -    iov.iov_base = buf;
        -    iov.iov_len = sizeof(buf);
        +        iovec iov;
        +        iov.iov_base = buf;
        +        iov.iov_len = sizeof(buf);
         
        -    msghdr msg;
        -    memset(&msg, 0, sizeof(msghdr));
        -    msg.msg_name = (sockaddr_in*)&peer;
        -    msg.msg_namelen = sizeof(sockaddr_in);
        -    msg.msg_iov = &iov;
        -    msg.msg_iovlen = 1;
        +        msghdr msg;
        +        memset(&msg, 0, sizeof(msghdr));
        +        msg.msg_name = (sockaddr_in*)&peer;
        +        msg.msg_namelen = sizeof(sockaddr_in);
        +        msg.msg_iov = &iov;
        +        msg.msg_iovlen = 1;
         
        -    int r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        -    assert(r0 > 0);
        -    printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        -        msg.msg_flags, msg.msg_iov->iov_base);
        +        int r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +        assert(r0 > 0);
        +        printf("Pong %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0,
        +            msg.msg_flags, msg.msg_iov->iov_base);
        +    }
         
             return NULL;
         }
         
        +void parse_reception(st_netfd_t stfd)
        +{
        +    msghdr msg;
        +    memset(&msg, 0, sizeof(msghdr));
        +
        +    // Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
        +    // See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393
        +    char control[100];
        +    msg.msg_control = control;
        +    msg.msg_controllen = sizeof(control);
        +    // Note that the r0 is 0, the reception is in the control.
        +    int r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT);
        +    assert(r0 >= 0);
        +    assert(msg.msg_flags == MSG_ERRQUEUE);
        +
        +    // Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing
        +    cmsghdr* cm = CMSG_FIRSTHDR(&msg);
        +    assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR);
        +
        +    sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm);
        +    assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY);
        +
        +    uint32_t hi = serr->ee_data;
        +    uint32_t lo = serr->ee_info;
        +    uint32_t range = hi - lo + 1;
        +    printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), range %d [%d, %d]\n",
        +        msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi);
        +
        +    // Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies
        +    if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) {
        +        printf("Warning: Defered copies, should stop zerocopy\n");
        +    }
        +}
        +
         int main(int argc, char** argv)
         {
        -    if (argc < 5) {
        -        printf("Usage: %s    \n", argv[0]);
        +    if (argc < 8) {
        +        printf("Usage: %s       \n", argv[0]);
        +        printf("    pong        Whether response pong, true|false\n");
        +        printf("    zerocopy    Whether use zerocopy to sendmsg, true|false\n");
        +        printf("    sendmmsg    The copies of message, 1 means sendmmsg(msg+msg)\n");
        +        printf("    loop        The number of loop to send out messages\n");
        +        printf("    batch       Whether read reception by batch, true|false\n");
                 printf("For example:\n");
        -        printf("        %s 127.0.0.1 8000 true true\n", argv[0]);
        +        printf("        %s 127.0.0.1 8000 true true 0 1 true\n", argv[0]);
                 exit(-1);
             }
         
        @@ -72,7 +113,11 @@ int main(int argc, char** argv)
             int port = atoi(argv[2]);
             bool pong = !strcmp(argv[3], "true");
             bool zerocopy = !strcmp(argv[4], "true");
        -    printf("Server listen %s:%d, pong %d, zerocopy %d\n", host, port, pong, zerocopy);
        +    int nn_copies = atoi(argv[5]);
        +    int loop = atoi(argv[6]);
        +    bool batch = !strcmp(argv[7], "true");
        +    printf("Server listen %s:%d, pong %d, zerocopy %d, copies %d, loop %d, batch %d\n",
        +        host, port, pong, zerocopy, nn_copies, loop, batch);
         
             assert(!st_set_eventsys(ST_EVENTSYS_ALT));
             assert(!st_init());
        @@ -121,51 +166,52 @@ int main(int argc, char** argv)
             iov.iov_base = buf;
             iov.iov_len = strlen(buf);
         
        -    msghdr msg;
        -    memset(&msg, 0, sizeof(msghdr));
        -    msg.msg_name = (sockaddr_in*)&peer;
        -    msg.msg_namelen = sizeof(sockaddr_in);
        -    msg.msg_iov = &iov;
        -    msg.msg_iovlen = 1;
        +    for (int k = 0; k < loop; k++) {
        +        msghdr msg;
        +        memset(&msg, 0, sizeof(msghdr));
        +        msg.msg_name = (sockaddr_in*)&peer;
        +        msg.msg_namelen = sizeof(sockaddr_in);
        +        msg.msg_iov = &iov;
        +        msg.msg_iovlen = 1;
         
        -    int r0;
        -    if (zerocopy) {
        -        r0 = st_sendmsg(stfd, &msg, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
        -    } else {
        -        r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +        int r0;
        +        if (nn_copies == 0) {
        +            if (zerocopy) {
        +                r0 = st_sendmsg(stfd, &msg, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
        +            } else {
        +                r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT);
        +            }
        +        } else {
        +            mmsghdr* hdrs = new mmsghdr[nn_copies + 1];
        +            for (int i = 0; i < nn_copies + 1; i++) {
        +                mmsghdr* p = hdrs + i;
        +                memcpy(&p->msg_hdr, &msg, sizeof(msghdr));
        +                p->msg_len = 0;
        +            }
        +            if (zerocopy) {
        +                r0 = st_sendmmsg(stfd, hdrs, nn_copies + 1, MSG_ZEROCOPY, ST_UTIME_NO_TIMEOUT);
        +            } else {
        +                r0 = st_sendmmsg(stfd, hdrs, nn_copies + 1, 0, ST_UTIME_NO_TIMEOUT);
        +            }
        +        }
        +        assert(r0 > 0);
        +        printf("Ping %s:%d %d bytes, copies=%d, r0=%d, %s\n", host, port, iov.iov_len, nn_copies, r0, msg.msg_iov->iov_base);
        +
        +        if (!zerocopy) {
        +            continue;
        +        }
        +
        +        if (!batch) {
        +            parse_reception(stfd);
        +        }
             }
        -    assert(r0 > 0);
        -    printf("Ping %s:%d %d bytes, r0=%d, %s\n", host, port, iov.iov_len, r0, msg.msg_iov->iov_base);
         
        -    // Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception
        -    // See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393
        -    char control[100];
        -    msg.msg_control = control;
        -    msg.msg_controllen = sizeof(control);
        -    // Note that the r0 is 0, the reception is in the control.
        -    r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT);
        -    assert(r0 >= 0);
        -    assert(msg.msg_flags == MSG_ERRQUEUE);
        -
        -    // Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing
        -    cmsghdr* cm = CMSG_FIRSTHDR(&msg);
        -    assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR);
        -
        -    sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm);
        -    assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY);
        -
        -    uint32_t hi = serr->ee_data;
        -    uint32_t lo = serr->ee_info;
        -    uint32_t range = hi - lo + 1;
        -    printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), %d [%d, %d]\n",
        -        msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi);
        -
        -    // Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies
        -    if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) {
        -        printf("Warning: Defered copies, should stop zerocopy\n");
        +    // @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-batching
        +    if (batch) {
        +        st_usleep(100 * 1000);
        +        parse_reception(stfd);
             }
         
             st_sleep(-1);
        -
             return 0;
         }
        
        From 6a070a9a64f454393e35c8fe985b6eaa8c5ef9e4 Mon Sep 17 00:00:00 2001
        From: kyxlx550 
        Date: Mon, 20 Apr 2020 21:34:18 +0800
        Subject: [PATCH 24/35] support sip ptz cmd
        
        ---
         trunk/research/players/srs_gb28181.html |  74 +++++++++-
         trunk/src/app/srs_app_gb28181.cpp       |  12 ++
         trunk/src/app/srs_app_gb28181.hpp       |   1 +
         trunk/src/app/srs_app_gb28181_sip.cpp   | 122 ++++++++++++++--
         trunk/src/app/srs_app_gb28181_sip.hpp   |   3 +
         trunk/src/app/srs_app_http_api.cpp      |  20 +++
         trunk/src/kernel/srs_kernel_error.hpp   |   3 +
         trunk/src/protocol/srs_sip_stack.cpp    | 182 ++++++++++++++++++++----
         trunk/src/protocol/srs_sip_stack.hpp    |  13 ++
         9 files changed, 387 insertions(+), 43 deletions(-)
        
        diff --git a/trunk/research/players/srs_gb28181.html b/trunk/research/players/srs_gb28181.html
        index 2962b4d4a..080ad0173 100644
        --- a/trunk/research/players/srs_gb28181.html
        +++ b/trunk/research/players/srs_gb28181.html
        @@ -318,11 +318,21 @@
                             
        +
        + + + + + + +
        + [注意] !!! 云台控制需要启用内部sip功能
        @PST
        + @@ -335,6 +345,17 @@ @@ -963,9 +984,13 @@ }); + var time_query = function(){ + $("#btn_sip_query_session").click(); + setTimeout(function () {$("#btn_sip_query_session").click()}, 1000); + } + $("#btn_sip_unregister").click(function(){ var id = $("#sipSessionId").text(); - alert(id); if (id.indexOf("id:") != -1) { var str = id.split(":") @@ -977,7 +1002,7 @@ $('#sipSessionMessage').html(syntaxHighlight(ret)); if (ret != undefined && ret.code == 0){ - $("#btn_sip_query_session").click(); + time_query(); } } }); @@ -996,7 +1021,7 @@ $('#sipSessionMessage').html(syntaxHighlight(ret)); if (ret != undefined && ret.code == 0){ - $("#btn_sip_query_session").click(); + time_query(); } } }); @@ -1015,7 +1040,7 @@ $('#sipSessionMessage').html(syntaxHighlight(ret)); if (ret != undefined && ret.code == 0){ - $("#btn_sip_query_session").click(); + time_query(); } } }); @@ -1033,7 +1058,7 @@ var ret = http_get(apiurl); $('#sipSessionMessage').html(syntaxHighlight(ret)); if (ret != undefined && ret.code == 0){ - $("#btn_sip_query_session").click(); + time_query(); } } }); @@ -1060,6 +1085,44 @@ } }); + var call_ptz_cmd = function(cmd) { + var str = $("#gb28181ChannelId").text(); + var str_array = str.split("@") + var chid = ""; + var id = ""; + if (str_array.length < 1){ + return; + } + + var speed = "136"; + + id = str_array[0]; + chid = str_array[1]; + + url = $("#txt_api_url").val(); + var apiurl = url + "/api/v1/gb28181?action=sip_ptz&id=" + id + "&chid="+chid+ "&ptzcmd="+cmd + "&speed=" + speed; + var ret = http_get(apiurl); + $('#gb28181ChannelMessage').html(syntaxHighlight(ret)); + }; + + var ptz_cmd = ["up", "down", "right", "left", "zoomin", "zoomout"] + for (var i=0; isend_bye(&req, chid); } +srs_error_t SrsGb28181Manger::notify_sip_ptz(std::string id, std::string chid, std::string cmd, + uint8_t speed, int priority) +{ + if (!sip_service){ + return srs_error_new(ERROR_GB28181_SIP_NOT_RUN, "sip not run"); + } + + SrsSipRequest req; + req.sip_auth_id = id; + return sip_service->send_ptz(&req, chid, cmd, speed, priority); +} + srs_error_t SrsGb28181Manger::notify_sip_raw_data(std::string id, std::string data) { if (!sip_service){ diff --git a/trunk/src/app/srs_app_gb28181.hpp b/trunk/src/app/srs_app_gb28181.hpp index 777e334eb..9f551a26a 100644 --- a/trunk/src/app/srs_app_gb28181.hpp +++ b/trunk/src/app/srs_app_gb28181.hpp @@ -422,6 +422,7 @@ public: srs_error_t notify_sip_raw_data(std::string id, std::string data); srs_error_t notify_sip_unregister(std::string id); srs_error_t notify_sip_query_catalog(std::string id); + srs_error_t notify_sip_ptz(std::string id, std::string chid, std::string cmd, uint8_t speed, int priority); srs_error_t query_sip_session(std::string id, SrsJsonArray* arr); private: diff --git a/trunk/src/app/srs_app_gb28181_sip.cpp b/trunk/src/app/srs_app_gb28181_sip.cpp index 36616cd4f..169cd1fca 100644 --- a/trunk/src/app/srs_app_gb28181_sip.cpp +++ b/trunk/src/app/srs_app_gb28181_sip.cpp @@ -100,6 +100,7 @@ SrsGb28181SipSession::SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipReques _peer_port = 0; _fromlen = 0; + _sip_cseq = 100; } SrsGb28181SipSession::~SrsGb28181SipSession() @@ -485,23 +486,30 @@ srs_error_t SrsGb28181SipService::on_udp_sip(string peer_ip, int peer_port, }else if (req->is_message()) { SrsGb28181SipSession* sip_session = fetch(session_id); + + if (!sip_session){ + sip_session = fetch_session_by_callid(req->call_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_alive_status(SrsGb28181SipSessionAliveOk); - sip_session->set_alive_time(srs_get_system_time()); - sip_session->set_sockaddr((sockaddr)*from); - sip_session->set_sockaddr_len(fromlen); - sip_session->set_peer_port(peer_port); - sip_session->set_peer_ip(peer_ip); - - //update device list - if (req->device_list_map.size() > 0){ - sip_session->update_device_list(req->device_list_map); + if (req->cmdtype == SrsSipCmdRequest){ + send_status(req, from, fromlen); + sip_session->set_alive_status(SrsGb28181SipSessionAliveOk); + sip_session->set_alive_time(srs_get_system_time()); + sip_session->set_sockaddr((sockaddr)*from); + sip_session->set_sockaddr_len(fromlen); + sip_session->set_peer_port(peer_port); + sip_session->set_peer_ip(peer_ip); + + //update device list + if (req->device_list_map.size() > 0){ + sip_session->update_device_list(req->device_list_map); + } } }else if (req->is_invite()) { @@ -675,6 +683,7 @@ srs_error_t SrsGb28181SipService::send_invite(SrsSipRequest *req, string ip, i req->realm = config->sip_realm; req->serial = config->sip_serial; req->chid = chid; + req->seq = sip_session->sip_cseq(); SrsSipRequest register_req = sip_session->request(); req->to_realm = register_req.to_realm; @@ -730,6 +739,7 @@ srs_error_t SrsGb28181SipService::send_bye(SrsSipRequest *req, std::string chid) req->realm = config->sip_realm; req->serial = config->sip_serial; req->chid = chid; + req->seq = sip_session->sip_cseq(); SrsSipRequest register_req = sip_session->request(); req->to_realm = register_req.to_realm; @@ -774,11 +784,22 @@ srs_error_t SrsGb28181SipService::send_sip_raw_data(SrsSipRequest *req, std::st srs_error_t SrsGb28181SipService::send_query_catalog(SrsSipRequest *req) { + srs_error_t err = srs_success; + + srs_assert(req); + + SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); + + if (!sip_session){ + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "sip session not exist"); + } + req->host = config->host; req->host_port = config->sip_port; req->realm = config->sip_realm; req->serial = config->sip_serial; req->chid = req->sip_auth_id; + req->seq = sip_session->sip_cseq(); //get protocol stack std::stringstream ss; @@ -787,6 +808,85 @@ srs_error_t SrsGb28181SipService::send_query_catalog(SrsSipRequest *req) return send_sip_raw_data(req, ss.str()); } +srs_error_t SrsGb28181SipService::send_ptz(SrsSipRequest *req, std::string chid, std::string cmd, + uint8_t speed, int priority) +{ + srs_error_t err = srs_success; + + srs_assert(req); + + SrsGb28181SipSession *sip_session = fetch(req->sip_auth_id); + + if (!sip_session){ + return srs_error_new(ERROR_GB28181_SESSION_IS_NOTEXIST, "sip session not exist"); + } + + SrsGb28181Device *device = sip_session->get_device_info(chid); + if (!device){ + return srs_error_new(ERROR_GB28181_SIP_CH_NOTEXIST, "sip device channel not exist"); + } + + if (device->invite_status != SrsGb28181SipSessionInviteOk){ + return srs_error_new(ERROR_GB28181_SIP_NOT_INVITE, "sip device channel not inviting"); + } + + //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(&device->req_inivate); + + req->host = config->host; + req->host_port = config->sip_port; + req->realm = config->sip_realm; + req->serial = config->sip_serial; + req->chid = chid; + req->seq = sip_session->sip_cseq(); + + SrsSipPtzCmdType ptzcmd = SrsSipPtzCmdRight; + const char *ss_cmd = cmd.c_str(); + if (!strcasecmp(ss_cmd, "stop")){ + ptzcmd = SrsSipPtzCmdStop; + }else if (!strcasecmp(ss_cmd, "right")){ + ptzcmd = SrsSipPtzCmdRight; + }else if (!strcasecmp(ss_cmd, "left")){ + ptzcmd = SrsSipPtzCmdLeft; + }else if (!strcasecmp(ss_cmd, "down")){ + ptzcmd = SrsSipPtzCmdDown; + }else if (!strcasecmp(ss_cmd, "up")){ + ptzcmd = SrsSipPtzCmdUp; + }else if (!strcasecmp(ss_cmd, "zoomout")){ + ptzcmd = SrsSipPtzCmdZoomOut; + }else if (!strcasecmp(ss_cmd, "zoomin")){ + ptzcmd = SrsSipPtzCmdZoomIn; + }else{ + return srs_error_new(ERROR_GB28181_SIP_PTZ_CMD_INVALID, "sip ptz cmd no support"); + } + + if (speed < 0 || speed > 0xFF){ + return srs_error_new(ERROR_GB28181_SIP_PTZ_CMD_INVALID, "sip ptz cmd speed out of range"); + } + + if (priority <= 0 ){ + priority = 5; + } + + //get protocol stack + std::stringstream ss; + sip->req_ptz(ss, req, ptzcmd, speed, priority); + + sockaddr addr = sip_session->sockaddr_from(); + if (send_message(&addr, sip_session->sockaddr_fromlen(), ss) <= 0) + { + return srs_error_new(ERROR_GB28181_SIP_PTZ_FAILED, "sip ptz failed"); + } + + //call_id map sip_session + sip_session_map_by_callid(sip_session, req->call_id); + + return err; + +} + srs_error_t SrsGb28181SipService::query_sip_session(std::string sid, SrsJsonArray* arr) { srs_error_t err = srs_success; diff --git a/trunk/src/app/srs_app_gb28181_sip.hpp b/trunk/src/app/srs_app_gb28181_sip.hpp index 952fd7a3c..10d180ee8 100644 --- a/trunk/src/app/srs_app_gb28181_sip.hpp +++ b/trunk/src/app/srs_app_gb28181_sip.hpp @@ -92,6 +92,7 @@ private: std::map _device_list; //std::map _device_status; + int _sip_cseq; public: SrsGb28181SipSession(SrsGb28181SipService *c, SrsSipRequest* r); @@ -128,6 +129,7 @@ public: sockaddr sockaddr_from() { return _from;} int sockaddr_fromlen() { return _fromlen;} SrsSipRequest request() { return *req;} + int sip_cseq(){ return _sip_cseq++;} std::string session_id() { return _session_id;} public: @@ -175,6 +177,7 @@ public: srs_error_t send_invite(SrsSipRequest *req, std::string ip, int port, uint32_t ssrc, std::string chid); srs_error_t send_bye(SrsSipRequest *req, std::string chid); srs_error_t send_query_catalog(SrsSipRequest *req); + srs_error_t send_ptz(SrsSipRequest *req, std::string chid, std::string cmd, uint8_t speed, int priority); // The SIP command is transmitted through HTTP API, // and the body content is transmitted to the device, diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index d0be9042c..da5ab0ad2 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1774,6 +1774,26 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa return srs_api_response_code(w, r, code); } + else if(action == "sip_ptz"){ + string chid = r->query_get("chid"); + string ptzcmd = r->query_get("ptzcmd"); + string speed = r->query_get("speed"); + string priority = r->query_get("priority"); + if (id.empty() || chid.empty() || ptzcmd.empty() || speed.empty()){ + return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); + } + + uint8_t _speed = (uint8_t)(strtoul(speed.c_str(), NULL, 10)); + int _priority = (int)(strtoul(priority.c_str(), NULL, 10)); + + err = _srs_gb28181->notify_sip_ptz(id, chid, ptzcmd, _speed, _priority); + int code = srs_error_code(err); + if (err != srs_success) { + srs_error_reset(err); + } + + 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); diff --git a/trunk/src/kernel/srs_kernel_error.hpp b/trunk/src/kernel/srs_kernel_error.hpp index 9b0e0578e..a60130a58 100644 --- a/trunk/src/kernel/srs_kernel_error.hpp +++ b/trunk/src/kernel/srs_kernel_error.hpp @@ -367,6 +367,9 @@ #define ERROR_GB28181_SIP_CH_NOTEXIST 6013 #define ERROR_GB28181_SIP_RAW_DATA_FAILED 6014 #define ERROR_GB28181_SIP_PRASE_FAILED 6015 +#define ERROR_GB28181_SIP_PTZ_FAILED 6016 +#define ERROR_GB28181_SIP_NOT_INVITE 6017 +#define ERROR_GB28181_SIP_PTZ_CMD_INVALID 6018 /////////////////////////////////////////////////////// // HTTP API error. diff --git a/trunk/src/protocol/srs_sip_stack.cpp b/trunk/src/protocol/srs_sip_stack.cpp index 466c69b2e..19d959723 100644 --- a/trunk/src/protocol/srs_sip_stack.cpp +++ b/trunk/src/protocol/srs_sip_stack.cpp @@ -45,10 +45,51 @@ using namespace std; unsigned int srs_sip_random(int min,int max) { - srand(int(time(0))); + //it is possible to duplicate data with time(0) + srand(unsigned(srs_update_system_time())); return rand() % (max - min + 1) + min; } +std::string srs_sip_generate_branch() +{ + int rand = srs_sip_random(10000000, 99999999); + std::stringstream branch; + branch << "SrsGbB" << rand; + return branch.str(); +} + +std::string srs_sip_generate_to_tag() +{ + uint32_t rand = srs_sip_random(10000000, 99999999); + std::stringstream branch; + branch << "SrsGbT" << rand; + return branch.str(); +} + +std::string srs_sip_generate_from_tag() +{ + uint32_t rand = srs_sip_random(10000000, 99999999); + std::stringstream branch; + branch << "SrsGbF" << rand; + return branch.str(); +} + +std::string srs_sip_generate_call_id() +{ + uint32_t rand = srs_sip_random(10000000, 99999999); + std::stringstream branch; + branch << "2020" << rand; + return branch.str(); +} + +std::string srs_sip_generate_sn() +{ + uint32_t rand = srs_sip_random(10000000, 99999999); + std::stringstream sn; + sn << rand; + return sn.str(); +} + std::string srs_sip_get_form_to_uri(std::string msg) { //;tag=536961166 @@ -897,15 +938,13 @@ void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, in << "y=" << _ssrc << SRS_RTSP_CRLF; - int rand = srs_sip_random(1000, 9999); - std::stringstream from, to, uri, branch, from_tag, call_id; + std::stringstream from, to, uri; //"INVITE sip:34020000001320000001@3402000000 SIP/2.0\r\n uri << "sip:" << req->chid << "@" << req->realm; //From: ;tag=500485%d\r\n from << req->serial << "@" << req->realm; to << req->chid << "@" << req->realm; - call_id << "2020" << rand ; - + req->from = from.str(); req->to = to.str(); @@ -918,19 +957,17 @@ void SrsSipStack::req_invite(stringstream& ss, SrsSipRequest *req, string ip, in } req->uri = uri.str(); - req->call_id = call_id.str(); - branch << "z9hG4bK3420" << rand; - from_tag << "51235" << rand; - req->branch = branch.str(); - req->from_tag = from_tag.str(); + req->call_id = srs_sip_generate_call_id(); + req->branch = srs_sip_generate_branch(); + req->from_tag = srs_sip_generate_from_tag(); ss << "INVITE " << req->uri << " " << SRS_SIP_VERSION << SRS_RTSP_CRLF << "Via: " << SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF << "From: " << get_sip_from(req) << SRS_RTSP_CRLF << "To: " << get_sip_to(req) << SRS_RTSP_CRLF << "Call-ID: " << req->call_id <seq << " INVITE" << SRS_RTSP_CRLF << "Content-Type: Application/SDP" << SRS_RTSP_CRLF << "Contact: to << ">" << SRS_RTSP_CRLF << "Max-Forwards: 70" << SRS_RTSP_CRLF @@ -1039,7 +1076,6 @@ void SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) req->uri = uri.str(); - int seq = srs_sip_random(22, 99); ss << "BYE " << req->uri << " "<< SRS_SIP_VERSION << SRS_RTSP_CRLF //<< "Via: "<< SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport" << branch << SRS_RTSP_CRLF << "Via: " << SRS_SIP_VERSION << "/UDP " << req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF @@ -1047,7 +1083,7 @@ void SrsSipStack::req_bye(std::stringstream& ss, SrsSipRequest *req) << "To: " << get_sip_to(req) << SRS_RTSP_CRLF //bye callid is inivte callid << "Call-ID: " << req->call_id << SRS_RTSP_CRLF - << "CSeq: "<< seq <<" BYE" << SRS_RTSP_CRLF + << "CSeq: "<< req->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; @@ -1149,40 +1185,134 @@ void SrsSipStack::req_query_catalog(std::stringstream& ss, SrsSipRequest *req) std::stringstream xml; std::string xmlbody; - int sn = srs_sip_random(10000000, 99999999); xml << "" << SRS_RTSP_CRLF << "" << SRS_RTSP_CRLF << "Catalog" << SRS_RTSP_CRLF - << "" << sn << "" << SRS_RTSP_CRLF + << "" << srs_sip_generate_sn() << "" << SRS_RTSP_CRLF << "" << req->sip_auth_id << "" << SRS_RTSP_CRLF << "" << SRS_RTSP_CRLF; xmlbody = xml.str(); - int rand = srs_sip_random(1000, 9999); - std::stringstream from, to, uri, branch, from_tag, call_id; + 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; - call_id << "2020" << rand; - + req->from = from.str(); req->to = to.str(); req->uri = uri.str(); - req->call_id = call_id.str(); - - branch << "z9hG4bK3420" << rand; - from_tag << "51235" << rand; - req->branch = branch.str(); - req->from_tag = from_tag.str(); + + req->call_id = srs_sip_generate_call_id(); + req->branch = srs_sip_generate_branch(); + req->from_tag = srs_sip_generate_from_tag(); ss << "MESSAGE " << req->uri << " " << SRS_SIP_VERSION << SRS_RTSP_CRLF << "Via: " << SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF << "From: " << get_sip_from(req) << SRS_RTSP_CRLF << "To: " << get_sip_to(req) << SRS_RTSP_CRLF << "Call-ID: " << req->call_id << SRS_RTSP_CRLF - << "CSeq: 25 MESSAGE" << SRS_RTSP_CRLF + << "CSeq: " << req->seq << " MESSAGE" << SRS_RTSP_CRLF + << "Content-Type: Application/MANSCDP+xml" << SRS_RTSP_CRLF + << "Max-Forwards: 70" << SRS_RTSP_CRLF + << "User-Agent: " << SRS_SIP_USER_AGENT << SRS_RTSP_CRLF + << "Content-Length: " << xmlbody.length() << SRS_RTSP_CRLFCRLF + << xmlbody; + +} + +void SrsSipStack::req_ptz(std::stringstream& ss, SrsSipRequest *req, uint8_t cmd, uint8_t speed, int priority) +{ + + /* + + + DeviceControl + 11 + 34020000001310000053 + A50F01021F0000D6 + + */ + + uint8_t ptz_cmd[8] = {0}; + ptz_cmd[0] = SRS_SIP_PTZ_START; + ptz_cmd[1] = 0x0F; + ptz_cmd[2] = 0x01; + ptz_cmd[3] = cmd; + switch(cmd){ + case SrsSipPtzCmdStop: // = 0x00 + ptz_cmd[4] = 0; + ptz_cmd[5] = 0; + ptz_cmd[6] = 0; + break; + case SrsSipPtzCmdRight: // = 0x01, + case SrsSipPtzCmdLeft: // = 0x02, + ptz_cmd[4] = speed; + break; + case SrsSipPtzCmdDown: // = 0x04, + case SrsSipPtzCmdUp: // = 0x08, + ptz_cmd[5] = speed; + break; + case SrsSipPtzCmdZoomOut: // = 0x10, + case SrsSipPtzCmdZoomIn: // = 0x20 + ptz_cmd[6] = (speed & 0x0F) << 4; + break; + default: + return; + } + + uint32_t check = 0; + for (int i = 0; i < 7; i++){ + check += ptz_cmd[i]; + } + + ptz_cmd[7] = (uint8_t)(check % 256); + + std::stringstream ss_ptzcmd; + for (int i = 0; i < 8; i++){ + char hex_cmd[3] = {0}; + sprintf(hex_cmd, "%02X", ptz_cmd[i]); + ss_ptzcmd << hex_cmd; + } + + std::stringstream xml; + std::string xmlbody; + + xml << "" << SRS_RTSP_CRLF + << "" << SRS_RTSP_CRLF + << "DeviceControl" << SRS_RTSP_CRLF + << "" << srs_sip_generate_sn() << "" << SRS_RTSP_CRLF + << "" << req->sip_auth_id << "" << SRS_RTSP_CRLF + << "" << ss_ptzcmd.str() << "" << SRS_RTSP_CRLF + << "" << SRS_RTSP_CRLF + << "" << priority << "" << SRS_RTSP_CRLF + << "" << SRS_RTSP_CRLF + << "" << SRS_RTSP_CRLF; + xmlbody = xml.str(); + + std::stringstream from, to, uri, call_id; + //"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(); + + req->call_id = srs_sip_generate_call_id(); + req->branch = srs_sip_generate_branch(); + req->from_tag = srs_sip_generate_from_tag(); + + ss << "MESSAGE " << req->uri << " "<< SRS_SIP_VERSION << SRS_RTSP_CRLF + //<< "Via: "<< SRS_SIP_VERSION << "/UDP "<< req->host << ":" << req->host_port << ";rport" << branch << SRS_RTSP_CRLF + << "Via: " << SRS_SIP_VERSION << "/UDP " << req->host << ":" << req->host_port << ";rport;branch=" << req->branch << SRS_RTSP_CRLF + << "From: " << get_sip_from(req) << SRS_RTSP_CRLF + << "To: " << get_sip_to(req) << SRS_RTSP_CRLF + << "Call-ID: " << req->call_id << SRS_RTSP_CRLF + << "CSeq: "<< req->seq <<" MESSAGE" << SRS_RTSP_CRLF << "Content-Type: Application/MANSCDP+xml" << SRS_RTSP_CRLF << "Max-Forwards: 70" << SRS_RTSP_CRLF << "User-Agent: " << SRS_SIP_USER_AGENT << SRS_RTSP_CRLF diff --git a/trunk/src/protocol/srs_sip_stack.hpp b/trunk/src/protocol/srs_sip_stack.hpp index 141a289ec..a91897d18 100644 --- a/trunk/src/protocol/srs_sip_stack.hpp +++ b/trunk/src/protocol/srs_sip_stack.hpp @@ -50,12 +50,24 @@ class SrsAudioFrame; #define SRS_SIP_VERSION "SIP/2.0" #define SRS_SIP_USER_AGENT RTMP_SIG_SRS_SERVER +#define SRS_SIP_PTZ_START 0xA5 + enum SrsSipCmdType{ SrsSipCmdRequest=0, SrsSipCmdRespone=1 }; +enum SrsSipPtzCmdType{ + SrsSipPtzCmdStop = 0x00, + SrsSipPtzCmdRight = 0x01, + SrsSipPtzCmdLeft = 0x02, + SrsSipPtzCmdDown = 0x04, + SrsSipPtzCmdUp = 0x08, + SrsSipPtzCmdZoomIn = 0x10, + SrsSipPtzCmdZoomOut = 0x20 +}; + std::string srs_sip_get_utc_date(); class SrsSipRequest @@ -162,6 +174,7 @@ public: virtual void req_bye(std::stringstream& ss, SrsSipRequest *req); virtual void req_401_unauthorized(std::stringstream& ss, SrsSipRequest *req); virtual void req_query_catalog(std::stringstream& ss, SrsSipRequest *req); + virtual void req_ptz(std::stringstream& ss, SrsSipRequest *req, uint8_t cmd, uint8_t speed, int priority); }; From 0fe9d9e3244a63cd22a5103f59e677789f33d4a2 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 12:12:32 +0800 Subject: [PATCH 25/35] Add UDP ZeroCopy research --- trunk/research/msg_zerocopy/client.cpp | 216 ++++++++++++++++++------- trunk/research/msg_zerocopy/server.cpp | 54 +++++-- 2 files changed, 198 insertions(+), 72 deletions(-) diff --git a/trunk/research/msg_zerocopy/client.cpp b/trunk/research/msg_zerocopy/client.cpp index cdfb18bc6..92f30f604 100644 --- a/trunk/research/msg_zerocopy/client.cpp +++ b/trunk/research/msg_zerocopy/client.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -30,6 +31,13 @@ #define MSG_ZEROCOPY 0x4000000 #endif +#include +// Define macro for UDP GSO. +// @see https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/udpgso.c +#ifndef UDP_SEGMENT +#define UDP_SEGMENT 103 +#endif + void* receiver(void* arg) { st_netfd_t stfd = (st_netfd_t)arg; @@ -61,63 +69,112 @@ void* receiver(void* arg) return NULL; } -void parse_reception(st_netfd_t stfd) +void parse_reception(st_netfd_t stfd, int nn_confirm) { - msghdr msg; - memset(&msg, 0, sizeof(msghdr)); + int left = nn_confirm; + while (left > 0) { + msghdr msg; + memset(&msg, 0, sizeof(msghdr)); - // Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception - // See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393 - char control[100]; - msg.msg_control = control; - msg.msg_controllen = sizeof(control); - // Note that the r0 is 0, the reception is in the control. - int r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT); - assert(r0 >= 0); - assert(msg.msg_flags == MSG_ERRQUEUE); + // Reception from kernel, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-reception + // See do_recv_completion at https://github.com/torvalds/linux/blob/master/tools/testing/selftests/net/msg_zerocopy.c#L393 + char control[100]; + msg.msg_control = control; + msg.msg_controllen = sizeof(control); + // Note that the r0 is 0, the reception is in the control. + int r0 = st_recvmsg(stfd, &msg, MSG_ERRQUEUE, ST_UTIME_NO_TIMEOUT); + assert(r0 >= 0); + assert(msg.msg_flags == MSG_ERRQUEUE); - // Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing - cmsghdr* cm = CMSG_FIRSTHDR(&msg); - assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR); + // Notification parsing, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-parsing + cmsghdr* cm = CMSG_FIRSTHDR(&msg); + assert(cm->cmsg_level == SOL_IP || cm->cmsg_type == IP_RECVERR); - sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm); - assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY); + sock_extended_err* serr = (sock_extended_err*)(void*)CMSG_DATA(cm); + assert(serr->ee_errno == 0 && serr->ee_origin == SO_EE_ORIGIN_ZEROCOPY); - uint32_t hi = serr->ee_data; - uint32_t lo = serr->ee_info; - uint32_t range = hi - lo + 1; - printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), range %d [%d, %d]\n", - msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi); + uint32_t hi = serr->ee_data; + uint32_t lo = serr->ee_info; + uint32_t range = hi - lo + 1; + left -= range; + printf("Reception %d bytes, flags %#x, cmsg(level %#x, type %#x), serr(errno %#x, origin %#x, code %#x), range %d [%d, %d]\n", + msg.msg_controllen, msg.msg_flags, cm->cmsg_level, cm->cmsg_type, serr->ee_errno, serr->ee_origin, serr->ee_code, range, lo, hi); - // Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies - if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) { - printf("Warning: Defered copies, should stop zerocopy\n"); + // Defered Copies, @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#deferred-copies + if (serr->ee_code == SO_EE_CODE_ZEROCOPY_COPIED) { + printf("Warning: Defered copies, should stop zerocopy\n"); + } } } +void usage(int argc, char** argv) +{ + printf("Usage: %s \n", argv[0]); + printf("Options:\n"); + printf(" --help Print this help and exit.\n"); + printf(" --host=string The host to send to.\n"); + printf(" --port=int The port to send to.\n"); + printf(" --pong=bool Whether response pong, true|false\n"); + printf(" --zerocopy=bool Whether use zerocopy to sendmsg, true|false\n"); + printf(" --copy=int The copies of message, 1 means sendmmsg(msg+msg)\n"); + printf(" --loop=int The number of loop to send out messages\n"); + printf(" --batch=bool Whether read reception by batch, true|false\n"); + printf(" --mix=bool Whether mix msg with zerocopy and those without, true|false\n"); + printf(" --size=int Each message size in bytes.\n"); + printf(" --gso=int The GSO size in bytes, 0 to disable it.\n"); + printf(" --iovs=int The number of iovs to send, at least 1.\n"); + printf(" --sndbuf=int The SO_SNDBUF size in bytes, 0 to ignore.\n"); + printf("For example:\n"); + printf(" %s --host=127.0.0.1 --port=8000 --pong=true --zerocopy=true --copy=0 --loop=1 --batch=true --mix=true --size=1400 --gso=0 --iovs=1 --sndbuf=0\n", argv[0]); +} + int main(int argc, char** argv) { - if (argc < 8) { - printf("Usage: %s \n", argv[0]); - printf(" pong Whether response pong, true|false\n"); - printf(" zerocopy Whether use zerocopy to sendmsg, true|false\n"); - printf(" sendmmsg The copies of message, 1 means sendmmsg(msg+msg)\n"); - printf(" loop The number of loop to send out messages\n"); - printf(" batch Whether read reception by batch, true|false\n"); - printf("For example:\n"); - printf(" %s 127.0.0.1 8000 true true 0 1 true\n", argv[0]); - exit(-1); + option longopts[] = { + { "host", required_argument, NULL, 'o' }, + { "port", required_argument, NULL, 'p' }, + { "pong", required_argument, NULL, 'n' }, + { "zerocopy", required_argument, NULL, 'z' }, + { "copy", required_argument, NULL, 'c' }, + { "loop", required_argument, NULL, 'l' }, + { "batch", required_argument, NULL, 'b' }, + { "mix", required_argument, NULL, 'm' }, + { "size", required_argument, NULL, 's' }, + { "gso", required_argument, NULL, 'g' }, + { "iovs", required_argument, NULL, 'i' }, + { "sndbuf", required_argument, NULL, 'u' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + char* host = NULL; char ch; + int port = 0; int nn_copies = 0; int loop = 1; int size = 1500; int gso = 0; int nn_iovs = 0; int sndbuf = 0; + bool pong = false; bool zerocopy = false; bool batch = false; bool mix = false; + while ((ch = getopt_long(argc, argv, "o:p:n:z:c:l:b:m:s:g:u:h", longopts, NULL)) != -1) { + switch (ch) { + case 'o': host = (char*)optarg; break; + case 'p': port = atoi(optarg); break; + case 'n': pong = !strcmp(optarg,"true"); break; + case 'z': zerocopy = !strcmp(optarg,"true"); break; + case 'c': nn_copies = atoi(optarg); break; + case 'l': loop = atoi(optarg); break; + case 'b': batch = !strcmp(optarg,"true"); break; + case 'm': mix = !strcmp(optarg,"true"); break; + case 's': size = atoi(optarg); break; + case 'g': gso = atoi(optarg); break; + case 'i': nn_iovs = atoi(optarg); break; + case 'u': sndbuf = atoi(optarg); break; + case 'h': usage(argc, argv); exit(0); + default: usage(argc, argv); exit(-1); + } } - char* host = argv[1]; - int port = atoi(argv[2]); - bool pong = !strcmp(argv[3], "true"); - bool zerocopy = !strcmp(argv[4], "true"); - int nn_copies = atoi(argv[5]); - int loop = atoi(argv[6]); - bool batch = !strcmp(argv[7], "true"); - printf("Server listen %s:%d, pong %d, zerocopy %d, copies %d, loop %d, batch %d\n", - host, port, pong, zerocopy, nn_copies, loop, batch); + printf("Server listen %s:%d, pong %d, zerocopy %d, copies %d, loop %d, batch %d, mix %d, size %d, gso %d, iovs %d, sndbuf %d\n", + host, port, pong, zerocopy, nn_copies, loop, batch, mix, size, gso, nn_iovs, sndbuf); + if (!host || !port || !nn_iovs) { + usage(argc, argv); + exit(-1); + } assert(!st_set_eventsys(ST_EVENTSYS_ALT)); assert(!st_init()); @@ -142,6 +199,21 @@ int main(int argc, char** argv) printf("epoll events EPOLLERR=%#x, EPOLLHUP=%#x\n", EPOLLERR, EPOLLHUP); } + if (true) { + int dv = 0; + socklen_t len = sizeof(dv); + int r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &dv, &len); + + int r1 = 0; + if (sndbuf > 0) { + r1 = setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)); + } + + int nv = 0; + int r2 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &nv, &len); + printf("socket SO_SNDBUF default=%d, user=%d, now=%d, r0=%d, r1=%d, r2=%d\n", dv, sndbuf, nv, r0, r1, r2); + } + st_netfd_t stfd = st_netfd_open_socket(fd); assert(stfd); printf("Client fd=%d\n", fd); @@ -158,21 +230,40 @@ int main(int argc, char** argv) peer.sin_port = htons(port); peer.sin_addr.s_addr = inet_addr(host); - char buf[1500]; - memset(buf, 0, sizeof(buf)); - memcpy(buf, "Hello", 5); + char* buf = new char[size]; + memset(buf, 0, size); + memcpy(buf, "Hello", size < 5? size : 5); iovec iov; iov.iov_base = buf; - iov.iov_len = strlen(buf); + iov.iov_len = size; + int nn_confirm = 0; for (int k = 0; k < loop; k++) { msghdr msg; memset(&msg, 0, sizeof(msghdr)); msg.msg_name = (sockaddr_in*)&peer; msg.msg_namelen = sizeof(sockaddr_in); - msg.msg_iov = &iov; - msg.msg_iovlen = 1; + msg.msg_iov = new iovec[nn_iovs]; + msg.msg_iovlen = nn_iovs; + + for (int i = 0; i < nn_iovs; i++) { + iovec* p = msg.msg_iov + i; + memcpy(p, &iov, sizeof(iovec)); + } + + if (gso > 0) { + msg.msg_controllen = CMSG_SPACE(sizeof(uint16_t)); + if (!msg.msg_control) { + msg.msg_control = new char[msg.msg_controllen]; + } + + cmsghdr* cm = CMSG_FIRSTHDR(&msg); + cm->cmsg_level = SOL_UDP; + cm->cmsg_type = UDP_SEGMENT; + cm->cmsg_len = CMSG_LEN(sizeof(uint16_t)); + *((uint16_t*)CMSG_DATA(cm)) = gso; + } int r0; if (nn_copies == 0) { @@ -194,22 +285,29 @@ int main(int argc, char** argv) r0 = st_sendmmsg(stfd, hdrs, nn_copies + 1, 0, ST_UTIME_NO_TIMEOUT); } } - assert(r0 > 0); - printf("Ping %s:%d %d bytes, copies=%d, r0=%d, %s\n", host, port, iov.iov_len, nn_copies, r0, msg.msg_iov->iov_base); - - if (!zerocopy) { - continue; + if (r0 > 0) { + printf("Ping %s:%d %d bytes, control %d, copies=%d, r0=%d, %s\n", host, port, iov.iov_len * nn_iovs, + msg.msg_controllen, nn_copies, r0, msg.msg_iov->iov_base); + } else { + printf("Ping %d bytes, error r0=%d, errno=%d\n", iov.iov_len * nn_iovs, r0, errno); exit(1); } - if (!batch) { - parse_reception(stfd); + if (zerocopy && !batch) { + parse_reception(stfd, r0); + } else { + nn_confirm += r0; + } + + if (mix) { + r0 = st_sendmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT); + assert(r0 > 0); + printf("Mix %s:%d %d bytes, r0=%d, %s\n", host, port, iov.iov_len * nn_iovs, r0, msg.msg_iov->iov_base); } } // @see https://www.kernel.org/doc/html/latest/networking/msg_zerocopy.html#notification-batching if (batch) { - st_usleep(100 * 1000); - parse_reception(stfd); + parse_reception(stfd, nn_confirm); } st_sleep(-1); diff --git a/trunk/research/msg_zerocopy/server.cpp b/trunk/research/msg_zerocopy/server.cpp index 6f83fde97..1b9a5d059 100644 --- a/trunk/research/msg_zerocopy/server.cpp +++ b/trunk/research/msg_zerocopy/server.cpp @@ -6,6 +6,7 @@ #include #include #include +#include struct message { st_netfd_t stfd; @@ -47,22 +48,48 @@ void* sender(void* arg) return NULL; } +void usage(int argc, char** argv) +{ + printf("Usage: %s \n", argv[0]); + printf("Options:\n"); + printf(" --help Print this help and exit.\n"); + printf(" --host=string The host to send to.\n"); + printf(" --port=int The port to send to.\n"); + printf(" --pong=bool Whether response pong, true|false\n"); + printf(" --delay=int The delay in ms to response pong.\n"); + printf("For example:\n"); + printf(" %s --host=0.0.0.0 --port=8000 --pong --delay=100\n", argv[0]); +} + int main(int argc, char** argv) { - if (argc < 5) { - printf("Usage: %s \n", argv[0]); - printf(" pong Whether response pong, true|false\n"); - printf(" delay The delay in ms to response pong.\n"); - printf("For example:\n"); - printf(" %s 0.0.0.0 8000 true 100\n", argv[0]); - exit(-1); + option longopts[] = { + { "host", required_argument, NULL, 'o' }, + { "port", required_argument, NULL, 'p' }, + { "pong", required_argument, NULL, 'n' }, + { "delay", required_argument, NULL, 'd' }, + { "help", no_argument, NULL, 'h' }, + { NULL, 0, NULL, 0 } + }; + + char* host = NULL; char ch; + int port = 0; int delay = 0; bool pong = false; + while ((ch = getopt_long(argc, argv, "o:p:n:d:h", longopts, NULL)) != -1) { + switch (ch) { + case 'o': host = (char*)optarg; break; + case 'p': port = atoi(optarg); break; + case 'n': pong = !strcmp(optarg,"true"); break; + case 'd': delay = atoi(optarg); break; + case 'h': usage(argc, argv); exit(0); + default: usage(argc, argv); exit(-1); + } } - char* host = argv[1]; - int port = atoi(argv[2]); - bool pong = !strcmp(argv[3], "true"); - int delay = ::atoi(argv[4]); printf("Server listen %s:%d, pong %d, delay: %dms\n", host, port, pong, delay); + if (!host || !port) { + usage(argc, argv); + exit(-1); + } assert(!st_set_eventsys(ST_EVENTSYS_ALT)); assert(!st_init()); @@ -107,11 +134,12 @@ int main(int argc, char** argv) msg.msg_iov = &iov; msg.msg_iovlen = 1; + int nn_msgs = 0; while (true) { r0 = st_recvmsg(stfd, &msg, 0, ST_UTIME_NO_TIMEOUT); assert(r0 > 0); - printf("From %s:%d %d bytes, flags %#x, %s\n", inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), r0, - msg.msg_flags, msg.msg_iov->iov_base); + printf("#%d, From %s:%d %d bytes, flags %#x, %s\n", nn_msgs++, inet_ntoa(peer.sin_addr), ntohs(peer.sin_port), + r0, msg.msg_flags, msg.msg_iov->iov_base); if (pong) { message* msg = new message(); From 511cf65ec8f43bf680d31405c945fa51a915190f Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 12:14:26 +0800 Subject: [PATCH 26/35] Add srs_recvmsg --- trunk/src/service/srs_service_st.cpp | 11 ++++++++--- trunk/src/service/srs_service_st.hpp | 1 + 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/trunk/src/service/srs_service_st.cpp b/trunk/src/service/srs_service_st.cpp index da54a5dae..d806db24e 100644 --- a/trunk/src/service/srs_service_st.cpp +++ b/trunk/src/service/srs_service_st.cpp @@ -94,7 +94,7 @@ srs_error_t srs_fd_closeexec(int fd) int flags = fcntl(fd, F_GETFD); flags |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, flags) == -1) { - return srs_error_new(ERROR_SOCKET_SETCLOSEEXEC, "FD_CLOEXEC fd=%v", fd); + return srs_error_new(ERROR_SOCKET_SETCLOSEEXEC, "FD_CLOEXEC fd=%d", fd); } return srs_success; @@ -104,7 +104,7 @@ srs_error_t srs_fd_reuseaddr(int fd) { int v = 1; if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(int)) == -1) { - return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEADDR fd=%v", fd); + return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEADDR fd=%d", fd); } return srs_success; @@ -119,7 +119,7 @@ srs_error_t srs_fd_reuseport(int fd) srs_warn("SO_REUSEPORT disabled for crossbuild"); return srs_success; #else - return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEPORT fd=%v", fd); + return srs_error_new(ERROR_SOCKET_SETREUSEADDR, "SO_REUSEPORT fd=%d", fd); #endif } #else @@ -402,6 +402,11 @@ int srs_sendto(srs_netfd_t stfd, void *buf, int len, const struct sockaddr * to, return st_sendto((st_netfd_t)stfd, buf, len, to, tolen, (st_utime_t)timeout); } +int srs_recvmsg(srs_netfd_t stfd, struct msghdr *msg, int flags, srs_utime_t timeout) +{ + return st_recvmsg((st_netfd_t)stfd, msg, flags, (st_utime_t)timeout); +} + int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout) { return st_sendmsg((st_netfd_t)stfd, msg, flags, (st_utime_t)timeout); diff --git a/trunk/src/service/srs_service_st.hpp b/trunk/src/service/srs_service_st.hpp index 784c58add..0986d553e 100644 --- a/trunk/src/service/srs_service_st.hpp +++ b/trunk/src/service/srs_service_st.hpp @@ -89,6 +89,7 @@ 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 *buf, int len, const struct sockaddr *to, int tolen, srs_utime_t timeout); +extern int srs_recvmsg(srs_netfd_t stfd, struct msghdr *msg, int flags, srs_utime_t timeout); extern int srs_sendmsg(srs_netfd_t stfd, const struct msghdr *msg, int flags, srs_utime_t timeout); #if !defined(SRS_AUTO_HAS_SENDMMSG) From 74800d0137eb19cd6c5f3c9b9f6205a8a7eb2400 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 12:18:49 +0800 Subject: [PATCH 27/35] Refactor code --- trunk/src/app/srs_app_config.cpp | 3 ++- trunk/src/app/srs_app_config.hpp | 9 +++------ trunk/src/app/srs_app_conn.cpp | 4 ++-- trunk/src/kernel/srs_kernel_file.cpp | 2 +- trunk/src/kernel/srs_kernel_mp4.cpp | 10 +++++----- 5 files changed, 13 insertions(+), 15 deletions(-) diff --git a/trunk/src/app/srs_app_config.cpp b/trunk/src/app/srs_app_config.cpp index 2a8454633..4f15e0356 100644 --- a/trunk/src/app/srs_app_config.cpp +++ b/trunk/src/app/srs_app_config.cpp @@ -4747,7 +4747,8 @@ int SrsConfig::get_rtc_server_sendmmsg() return DEFAULT; } - return ::atoi(conf->arg0().c_str()); + int v = ::atoi(conf->arg0().c_str()); + return srs_max(1, v); #endif } diff --git a/trunk/src/app/srs_app_config.hpp b/trunk/src/app/srs_app_config.hpp index a905949b1..5401c29a2 100644 --- a/trunk/src/app/srs_app_config.hpp +++ b/trunk/src/app/srs_app_config.hpp @@ -528,17 +528,14 @@ public: virtual int get_rtc_server_sendmmsg(); virtual bool get_rtc_server_encrypt(); virtual int get_rtc_server_reuseport(); -private: - virtual int get_rtc_server_reuseport2(); -public: virtual bool get_rtc_server_merge_nalus(); virtual bool get_rtc_server_gso(); -private: - virtual bool get_rtc_server_gso2(); -public: virtual int get_rtc_server_padding(); virtual bool get_rtc_server_perf_stat(); virtual int get_rtc_server_queue_length(); +private: + virtual int get_rtc_server_reuseport2(); + virtual bool get_rtc_server_gso2(); public: SrsConfDirective* get_rtc(std::string vhost); diff --git a/trunk/src/app/srs_app_conn.cpp b/trunk/src/app/srs_app_conn.cpp index aa3f8300a..4018ac512 100644 --- a/trunk/src/app/srs_app_conn.cpp +++ b/trunk/src/app/srs_app_conn.cpp @@ -103,7 +103,7 @@ srs_error_t SrsConnection::set_tcp_nodelay(bool v) int iv = (v? 1:0); if ((r0 = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, nb_v)) != 0) { - return srs_error_new(ERROR_SOCKET_NO_NODELAY, "setsockopt fd=%d, r0=%v", fd, r0); + return srs_error_new(ERROR_SOCKET_NO_NODELAY, "setsockopt fd=%d, r0=%d", fd, r0); } if ((r0 = getsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &iv, &nb_v)) != 0) { return srs_error_new(ERROR_SOCKET_NO_NODELAY, "getsockopt fd=%d, r0=%d", fd, r0); @@ -155,7 +155,7 @@ srs_error_t SrsConnection::set_socket_buffer(srs_utime_t buffer_v) // set the socket send buffer when required larger buffer if (setsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, nb_v) < 0) { - return srs_error_new(ERROR_SOCKET_SNDBUF, "setsockopt fd=%d, r0=%v", fd, r0); + return srs_error_new(ERROR_SOCKET_SNDBUF, "setsockopt fd=%d, r0=%d", fd, r0); } if ((r0 = getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &iv, &nb_v)) != 0) { return srs_error_new(ERROR_SOCKET_SNDBUF, "getsockopt fd=%d, r0=%d", fd, r0); diff --git a/trunk/src/kernel/srs_kernel_file.cpp b/trunk/src/kernel/srs_kernel_file.cpp index 194a5e00e..95e1139fa 100644 --- a/trunk/src/kernel/srs_kernel_file.cpp +++ b/trunk/src/kernel/srs_kernel_file.cpp @@ -296,7 +296,7 @@ srs_error_t SrsFileReader::lseek(off_t offset, int whence, off_t* seeked) { off_t sk = _srs_lseek_fn(fd, offset, whence); if (sk < 0) { - return srs_error_new(ERROR_SYSTEM_FILE_SEEK, "seek %v failed", (int)sk); + return srs_error_new(ERROR_SYSTEM_FILE_SEEK, "seek %d failed", (int)sk); } if (seeked) { diff --git a/trunk/src/kernel/srs_kernel_mp4.cpp b/trunk/src/kernel/srs_kernel_mp4.cpp index 5f83d0e47..a41f27f56 100644 --- a/trunk/src/kernel/srs_kernel_mp4.cpp +++ b/trunk/src/kernel/srs_kernel_mp4.cpp @@ -510,7 +510,7 @@ srs_error_t SrsMp4Box::encode_header(SrsBuffer* buf) int lrsz = nb_header() - SrsMp4Box::nb_header(); if (!buf->require(lrsz)) { - return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "box requires %v only %d bytes", lrsz, buf->left()); + return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "box requires %d only %d bytes", lrsz, buf->left()); } return err; @@ -3602,19 +3602,19 @@ srs_error_t SrsMp4ES_Descriptor::decode_payload(SrsBuffer* buf) if (streamDependenceFlag) { if (!buf->require(2)) { - return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "ES requires 2 only %v bytes", buf->left()); + return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "ES requires 2 only %d bytes", buf->left()); } dependsOn_ES_ID = buf->read_2bytes(); } if (URL_Flag) { if (!buf->require(1)) { - return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URLlength requires 1 only %v bytes", buf->left()); + return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URLlength requires 1 only %d bytes", buf->left()); } uint8_t URLlength = buf->read_1bytes(); if (!buf->require(URLlength)) { - return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URL requires %d only %v bytes", URLlength, buf->left()); + return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "URL requires %d only %d bytes", URLlength, buf->left()); } URLstring.resize(URLlength); buf->read_bytes(&URLstring[0], URLlength); @@ -3622,7 +3622,7 @@ srs_error_t SrsMp4ES_Descriptor::decode_payload(SrsBuffer* buf) if (OCRstreamFlag) { if (!buf->require(2)) { - return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "OCR requires 2 only %v bytes", buf->left()); + return srs_error_new(ERROR_MP4_BOX_REQUIRE_SPACE, "OCR requires 2 only %d bytes", buf->left()); } OCR_ES_Id = buf->read_2bytes(); } From 904ce2452ba5e6e514f447ba3997e63f21f6a43d Mon Sep 17 00:00:00 2001 From: kyxlx550 Date: Tue, 21 Apr 2020 12:53:52 +0800 Subject: [PATCH 28/35] update srs gb28281 web demo --- trunk/research/players/srs_gb28181.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/trunk/research/players/srs_gb28181.html b/trunk/research/players/srs_gb28181.html index 080ad0173..4cf355f11 100644 --- a/trunk/research/players/srs_gb28181.html +++ b/trunk/research/players/srs_gb28181.html @@ -77,7 +77,7 @@
        sip会话(需内部启用sip) -
        +
          @@ -92,7 +92,7 @@
          当前会话:
          -
          
          +                    
          
                             
          @@ -104,7 +104,7 @@
          GB28181媒体通道 -
          +
            @@ -128,7 +128,7 @@
            -
            
            +                    
            
                               
            From bff93c3f6a7b7a870877d8bbae84738f8aa53755 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 13:30:00 +0800 Subject: [PATCH 29/35] Update ST doc --- trunk/3rdparty/st-srs/README.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/trunk/3rdparty/st-srs/README.md b/trunk/3rdparty/st-srs/README.md index 8ea593b39..b05a6d86c 100644 --- a/trunk/3rdparty/st-srs/README.md +++ b/trunk/3rdparty/st-srs/README.md @@ -31,6 +31,14 @@ The branch [srs](https://github.com/ossrs/state-threads/tree/srs) will be patche * API reference: http://ossrs.github.io/state-threads/docs/reference.html * Programming notes: http://ossrs.github.io/state-threads/docs/notes.html +## Analysis + +* About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg). +* About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg) +* About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25). +* About the scheduler, read [#13-scheduler](https://github.com/ossrs/state-threads/issues/13#issuecomment-616025527). +* About the IO event system, read [#13-IO](https://github.com/ossrs/state-threads/issues/13#issuecomment-616096568). + ## Usage Get code: @@ -87,12 +95,4 @@ Important cli options: 1. `--track-origins= [default: no]`, Controls whether Memcheck tracks the origin of uninitialised values. By default, it does not, which means that although it can tell you that an uninitialised value is being used in a dangerous way, it cannot tell you where the uninitialised value came from. This often makes it difficult to track down the root problem. 1. `--show-reachable= , --show-possibly-lost=`, to show the using memory. -## Analysis - -1. About setjmp and longjmp, read [setjmp](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-setjmp.jpg). -1. About the stack structure, read [stack](https://gitee.com/winlinvip/srs-wiki/raw/master/images/st-stack.jpg) -1. About asm code comments, read [#91d530e](https://github.com/ossrs/state-threads/commit/91d530e#diff-ed9428b14ff6afda0e9ab04cc91d4445R25). -1. About the scheduler, read [#13-scheduler](https://github.com/ossrs/state-threads/issues/13#issuecomment-616025527). -1. About the IO event system, read [#13-IO](https://github.com/ossrs/state-threads/issues/13#issuecomment-616096568). - Winlin 2016 From 244fefa8c58ede28dbedc60c122b656569beaa87 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 15:06:57 +0800 Subject: [PATCH 30/35] Update utest --- trunk/src/utest/srs_utest_amf0.cpp | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/trunk/src/utest/srs_utest_amf0.cpp b/trunk/src/utest/srs_utest_amf0.cpp index a76f22464..4b65e13c4 100644 --- a/trunk/src/utest/srs_utest_amf0.cpp +++ b/trunk/src/utest/srs_utest_amf0.cpp @@ -526,6 +526,8 @@ VOID TEST(ProtocolAMF0Test, ApiAnyElem) */ VOID TEST(ProtocolAMF0Test, ApiAnyIO) { + srs_error_t err; + SrsAmf0Any* o = NULL; char buf[1024]; @@ -539,14 +541,15 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) o = SrsAmf0Any::object_eof(); SrsAutoFree(SrsAmf0Any, o); - - EXPECT_EQ(srs_success, o->read(&s)); + + HELPER_EXPECT_SUCCESS(o->read(&s)); EXPECT_EQ(o->total_size(), s.pos()); EXPECT_EQ(3, s.pos()); s.skip(-1 * s.pos()); (s.data() + s.pos())[0] = 0x01; - EXPECT_NE(srs_success, o->read(&s)); + + HELPER_EXPECT_FAILED(o->read(&s)); } if (true) { s.skip(-1 * s.pos()); @@ -580,7 +583,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) s.skip(-1 * s.pos()); (s.data() + s.pos())[3] = 'x'; - EXPECT_EQ(srs_success, o->read(&s)); + HELPER_EXPECT_SUCCESS(o->read(&s)); EXPECT_EQ(o->total_size(), s.pos()); EXPECT_STREQ("xinlin", o->to_str().c_str()); } @@ -599,7 +602,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) EXPECT_EQ(0, s.read_1bytes()); s.skip(-1 * s.pos()); - EXPECT_EQ(srs_success, o->read(&s)); + HELPER_EXPECT_SUCCESS(o->read(&s)); EXPECT_EQ(o->total_size(), s.pos()); EXPECT_DOUBLE_EQ(10, o->to_number()); } @@ -618,7 +621,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) EXPECT_EQ(1, s.read_1bytes()); s.skip(-1 * s.pos()); - EXPECT_EQ(srs_success, o->read(&s)); + HELPER_EXPECT_SUCCESS(o->read(&s)); EXPECT_EQ(o->total_size(), s.pos()); EXPECT_TRUE(o->to_boolean()); } @@ -635,7 +638,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) EXPECT_EQ(1, s.read_1bytes()); s.skip(-1 * s.pos()); - EXPECT_EQ(srs_success, o->read(&s)); + HELPER_EXPECT_SUCCESS(o->read(&s)); EXPECT_EQ(o->total_size(), s.pos()); EXPECT_FALSE(o->to_boolean()); } @@ -654,7 +657,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) EXPECT_EQ(5, s.read_1bytes()); s.skip(-1 * s.pos()); - EXPECT_EQ(srs_success, o->read(&s)); + HELPER_EXPECT_SUCCESS(o->read(&s)); EXPECT_EQ(o->total_size(), s.pos()); EXPECT_TRUE(o->is_null()); } @@ -673,7 +676,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) EXPECT_EQ(6, s.read_1bytes()); s.skip(-1 * s.pos()); - EXPECT_EQ(srs_success, o->read(&s)); + HELPER_EXPECT_SUCCESS(o->read(&s)); EXPECT_EQ(o->total_size(), s.pos()); EXPECT_TRUE(o->is_undefined()); } @@ -838,6 +841,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyIO) */ VOID TEST(ProtocolAMF0Test, ApiAnyTypeAssert) { + srs_error_t err; SrsAmf0Any* o = NULL; char buf[1024]; @@ -848,7 +852,7 @@ VOID TEST(ProtocolAMF0Test, ApiAnyTypeAssert) if (true) { s.skip(-1 * s.pos()); (s.data() + s.pos())[0] = 0x12; - EXPECT_NE(srs_success, srs_amf0_read_any(&s, &o)); + HELPER_EXPECT_FAILED(srs_amf0_read_any(&s, &o)); EXPECT_TRUE(NULL == o); srs_freep(o); } From 7bec73f2249493933dbafcdae9424a1d7b280df9 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 15:13:46 +0800 Subject: [PATCH 31/35] Refactor demo pages. --- trunk/research/players/rtc_player.html | 11 ++++++----- trunk/research/players/srs_gb28181.html | 11 ++++++----- trunk/research/players/srs_player.html | 11 ++++++----- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/trunk/research/players/rtc_player.html b/trunk/research/players/rtc_player.html index 719d710ea..e531aa434 100644 --- a/trunk/research/players/rtc_player.html +++ b/trunk/research/players/rtc_player.html @@ -24,13 +24,14 @@
            diff --git a/trunk/research/players/srs_gb28181.html b/trunk/research/players/srs_gb28181.html index 4cf355f11..702cf8473 100644 --- a/trunk/research/players/srs_gb28181.html +++ b/trunk/research/players/srs_gb28181.html @@ -37,13 +37,14 @@ diff --git a/trunk/research/players/srs_player.html b/trunk/research/players/srs_player.html index 2f5a2c4b8..84b78b5c8 100755 --- a/trunk/research/players/srs_player.html +++ b/trunk/research/players/srs_player.html @@ -34,13 +34,14 @@ From 82396ec112b6b0533fcc34ce619e015bd54914be Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 15:25:50 +0800 Subject: [PATCH 32/35] Refactor GB28181 code --- trunk/src/app/srs_app_http_api.cpp | 312 ++++++++++++++--------------- trunk/src/app/srs_app_http_api.hpp | 2 + 2 files changed, 150 insertions(+), 164 deletions(-) diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index da5ab0ad2..b939b209e 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1672,6 +1672,19 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa { srs_error_t err = srs_success; + if ((err = do_serve_http(w, r)) != srs_success) { + srs_warn("Server GB28181 err %s", srs_error_desc(err).c_str()); + int code = srs_error_code(err); srs_error_reset(err); + return srs_api_response_code(w, r, code); + } + + return err; +} + +srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) +{ + srs_error_t err = srs_success; + SrsJsonObject* obj = SrsJsonAny::object(); SrsAutoFree(SrsJsonObject, obj); @@ -1687,173 +1700,144 @@ srs_error_t SrsGoApiGb28181::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessa //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); - } + if (!_srs_gb28181) { + return srs_error_new(ERROR_GB28181_SERVER_NOT_RUN, "no gb28181 engine"); + } - SrsGb28181StreamChannel channel; - channel.set_channel_id(id); - channel.set_app(app); - channel.set_stream(stream); - channel.set_port_mode(port_mode); - - if ((err =_srs_gb28181->create_stream_channel(&channel)) != srs_success) { - int code = srs_error_code(err); srs_error_reset(err); - 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); - } - - err =_srs_gb28181->delete_stream_channel(id); - int code = srs_error_code(err); - if (err != srs_success) { - srs_error_reset(err); - } - - return srs_api_response_code(w, r, code); - } - else if(action == "query_channel") { - SrsJsonArray* arr = SrsJsonAny::array(); - data->set("channels", arr); - - if ((err = _srs_gb28181->query_stream_channel(id, arr)) != srs_success) { - int code = srs_error_code(err); srs_error_reset(err); - return srs_api_response_code(w, r, code); - } - - return srs_api_response(w, r, obj->dumps()); - } - else if(action == "sip_invite"){ - string chid = r->query_get("chid"); - if (id.empty() || chid.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)); - - err = _srs_gb28181->notify_sip_invite(id, ip, _port, _ssrc, chid); - int code = srs_error_code(err); - if (err != srs_success) { - srs_error_reset(err); - } - - return srs_api_response_code(w, r, code); - } - else if(action == "sip_bye"){ - string chid = r->query_get("chid"); - if (id.empty() || chid.empty()){ - return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); - } - - err = _srs_gb28181->notify_sip_bye(id, chid); - int code = srs_error_code(err); - if (err != srs_success) { - srs_error_reset(err); - } - - return srs_api_response_code(w, r, code); - } - else if(action == "sip_ptz"){ - string chid = r->query_get("chid"); - string ptzcmd = r->query_get("ptzcmd"); - string speed = r->query_get("speed"); - string priority = r->query_get("priority"); - if (id.empty() || chid.empty() || ptzcmd.empty() || speed.empty()){ - return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); - } - - uint8_t _speed = (uint8_t)(strtoul(speed.c_str(), NULL, 10)); - int _priority = (int)(strtoul(priority.c_str(), NULL, 10)); - - err = _srs_gb28181->notify_sip_ptz(id, chid, ptzcmd, _speed, _priority); - int code = srs_error_code(err); - if (err != srs_success) { - srs_error_reset(err); - } - - 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); - - err = _srs_gb28181->notify_sip_raw_data(id, body); - int code = srs_error_code(err); - if (err != srs_success) { - srs_error_reset(err); - } - - 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); - } - - err = _srs_gb28181->notify_sip_unregister(id); - int code = srs_error_code(err); - if (err != srs_success) { - srs_error_reset(err); - } - - return srs_api_response_code(w, r, code); - } - else if(action == "sip_query_catalog"){ - if (id.empty()){ - return srs_api_response_code(w, r, ERROR_GB28181_VALUE_EMPTY); - } - - err = _srs_gb28181->notify_sip_query_catalog(id); - int code = srs_error_code(err); - if (err != srs_success) { - srs_error_reset(err); - } - - return srs_api_response_code(w, r, code); - } - else if(action == "sip_query_session"){ - SrsJsonArray* arr = SrsJsonAny::array(); - data->set("sessions", arr); - - if ((err = _srs_gb28181->query_sip_session(id, arr)) != srs_success) { - int code = srs_error_code(err); srs_error_reset(err); - return srs_api_response_code(w, r, code); - } - - return srs_api_response(w, r, obj->dumps()); - } - else - { - return srs_api_response_code(w, r, ERROR_GB28181_ACTION_INVALID); + if(action == "create_channel"){ + if (id.empty()){ + return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); } - }else { - return srs_api_response_code(w, r, ERROR_GB28181_SERVER_NOT_RUN); + SrsGb28181StreamChannel channel; + channel.set_channel_id(id); + channel.set_app(app); + channel.set_stream(stream); + channel.set_port_mode(port_mode); + + if ((err = _srs_gb28181->create_stream_channel(&channel)) != srs_success) { + return srs_error_wrap(err, "create stream channel"); + } + + 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_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); + } + + if ((err = _srs_gb28181->delete_stream_channel(id)) != srs_success) { + return srs_error_wrap(err, "delete stream channel"); + } + + return srs_api_response_code(w, r, code); + } else if(action == "query_channel") { + SrsJsonArray* arr = SrsJsonAny::array(); + data->set("channels", arr); + + if ((err = _srs_gb28181->query_stream_channel(id, arr)) != srs_success) { + return srs_error_wrap(err, "query stream channel"); + } + + return srs_api_response(w, r, obj->dumps()); + } else if(action == "sip_invite"){ + string chid = r->query_get("chid"); + if (id.empty() || chid.empty()){ + return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id or chid"); + } + + 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)); + + if ((err = _srs_gb28181->notify_sip_invite(id, ip, _port, _ssrc, chid)) != srs_success) { + return srs_error_wrap(err, "notify sip invite"); + } + + return srs_api_response_code(w, r, code); + } else if(action == "sip_bye"){ + string chid = r->query_get("chid"); + if (id.empty() || chid.empty()){ + return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id or chid"); + } + + if ((err = _srs_gb28181->notify_sip_bye(id, chid)) != srs_success) { + return srs_error_wrap(err, "notify sip bye"); + } + + return srs_api_response_code(w, r, code); + } else if(action == "sip_ptz"){ + string chid = r->query_get("chid"); + string ptzcmd = r->query_get("ptzcmd"); + string speed = r->query_get("speed"); + string priority = r->query_get("priority"); + if (id.empty() || chid.empty() || ptzcmd.empty() || speed.empty()){ + return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id or chid or ptzcmd or speed"); + } + + uint8_t _speed = (uint8_t)(strtoul(speed.c_str(), NULL, 10)); + int _priority = (int)(strtoul(priority.c_str(), NULL, 10)); + + if ((err = _srs_gb28181->notify_sip_ptz(id, chid, ptzcmd, _speed, _priority)) != srs_success) { + return srs_error_wrap(err, "notify sip ptz"); + } + + return srs_api_response_code(w, r, code); + } else if(action == "sip_raw_data"){ + if (id.empty()){ + return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); + } + + std::string body; + r->body_read_all(body); + + if ((err = _srs_gb28181->notify_sip_raw_data(id, body)) != srs_success) { + return srs_error_wrap(err, "notify sip raw data"); + } + + return srs_api_response_code(w, r, code); + } else if(action == "sip_unregister"){ + if (id.empty()){ + return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); + } + + if ((err = _srs_gb28181->notify_sip_unregister(id)) != srs_success) { + return srs_error_wrap(err, "notify sip unregister"); + } + + return srs_api_response_code(w, r, code); + } else if(action == "sip_query_catalog"){ + if (id.empty()){ + return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); + } + + if ((err = _srs_gb28181->notify_sip_query_catalog(id)) != srs_success) { + return srs_error_wrap(err, "notify sip query catelog"); + } + + return srs_api_response_code(w, r, code); + } else if(action == "sip_query_session"){ + SrsJsonArray* arr = SrsJsonAny::array(); + data->set("sessions", arr); + + if ((err = _srs_gb28181->query_sip_session(id, arr)) != srs_success) { + return srs_error_wrap(err, "notify sip session"); + } + + return srs_api_response(w, r, obj->dumps()); + } else { + return srs_error_new(ERROR_GB28181_ACTION_INVALID, "action %s", action.c_str()); } } #endif diff --git a/trunk/src/app/srs_app_http_api.hpp b/trunk/src/app/srs_app_http_api.hpp index 6dbed63d5..b94dfa46c 100644 --- a/trunk/src/app/srs_app_http_api.hpp +++ b/trunk/src/app/srs_app_http_api.hpp @@ -249,6 +249,8 @@ public: virtual ~SrsGoApiGb28181(); public: virtual srs_error_t serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); +private: + virtual srs_error_t do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; #endif From 854247e9793f3e28e497c89e7868f4d254731ae1 Mon Sep 17 00:00:00 2001 From: winlin Date: Tue, 21 Apr 2020 15:38:31 +0800 Subject: [PATCH 33/35] Fix GB28181 build failed. --- trunk/src/app/srs_app_http_api.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/trunk/src/app/srs_app_http_api.cpp b/trunk/src/app/srs_app_http_api.cpp index 5400acac9..071a0b8b5 100644 --- a/trunk/src/app/srs_app_http_api.cpp +++ b/trunk/src/app/srs_app_http_api.cpp @@ -1809,7 +1809,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe return srs_error_wrap(err, "delete stream channel"); } - return srs_api_response_code(w, r, code); + return srs_api_response_code(w, r, 0); } else if(action == "query_channel") { SrsJsonArray* arr = SrsJsonAny::array(); data->set("channels", arr); @@ -1836,7 +1836,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe return srs_error_wrap(err, "notify sip invite"); } - return srs_api_response_code(w, r, code); + return srs_api_response_code(w, r, 0); } else if(action == "sip_bye"){ string chid = r->query_get("chid"); if (id.empty() || chid.empty()){ @@ -1847,7 +1847,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe return srs_error_wrap(err, "notify sip bye"); } - return srs_api_response_code(w, r, code); + return srs_api_response_code(w, r, 0); } else if(action == "sip_ptz"){ string chid = r->query_get("chid"); string ptzcmd = r->query_get("ptzcmd"); @@ -1864,7 +1864,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe return srs_error_wrap(err, "notify sip ptz"); } - return srs_api_response_code(w, r, code); + return srs_api_response_code(w, r, 0); } else if(action == "sip_raw_data"){ if (id.empty()){ return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); @@ -1877,7 +1877,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe return srs_error_wrap(err, "notify sip raw data"); } - return srs_api_response_code(w, r, code); + return srs_api_response_code(w, r, 0); } else if(action == "sip_unregister"){ if (id.empty()){ return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); @@ -1887,7 +1887,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe return srs_error_wrap(err, "notify sip unregister"); } - return srs_api_response_code(w, r, code); + return srs_api_response_code(w, r, 0); } else if(action == "sip_query_catalog"){ if (id.empty()){ return srs_error_new(ERROR_GB28181_VALUE_EMPTY, "no id"); @@ -1897,7 +1897,7 @@ srs_error_t SrsGoApiGb28181::do_serve_http(ISrsHttpResponseWriter* w, ISrsHttpMe return srs_error_wrap(err, "notify sip query catelog"); } - return srs_api_response_code(w, r, code); + return srs_api_response_code(w, r, 0); } else if(action == "sip_query_session"){ SrsJsonArray* arr = SrsJsonAny::array(); data->set("sessions", arr); From 38f7299a51f1fbc88c2796e3e36831be4ad4d509 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 22 Apr 2020 09:28:13 +0800 Subject: [PATCH 34/35] Remove double check for FFMPEG --- trunk/auto/options.sh | 4 ---- 1 file changed, 4 deletions(-) diff --git a/trunk/auto/options.sh b/trunk/auto/options.sh index 2a3b2480b..745b45e29 100755 --- a/trunk/auto/options.sh +++ b/trunk/auto/options.sh @@ -656,10 +656,6 @@ function check_option_conflicts() { echo "Don't support building NGINX, please use docker https://github.com/ossrs/srs-docker"; exit -1; fi - if [[ $SRS_FFMPEG_TOOL == YES ]]; then - echo "Don't support building FFMPEG, please use docker https://github.com/ossrs/srs-docker"; exit -1; - fi - # For OSX, recommend to use DTrace, https://blog.csdn.net/win_lin/article/details/53503869 if [[ $SRS_OSX == YES && $SRS_GPROF == YES ]]; then echo "Tool gprof for OSX is unavailable, please use dtrace, read https://blog.csdn.net/win_lin/article/details/53503869" From ee1002fc3dce102cfc04fcb46c757f1b2a9470a8 Mon Sep 17 00:00:00 2001 From: winlin Date: Wed, 22 Apr 2020 11:39:18 +0800 Subject: [PATCH 35/35] Fix OSX utest failed. --- trunk/src/utest/srs_utest_kernel.cpp | 4 ++++ trunk/src/utest/srs_utest_service.cpp | 31 ++++++++++++++++++++++++++- 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/trunk/src/utest/srs_utest_kernel.cpp b/trunk/src/utest/srs_utest_kernel.cpp index 72395fc61..7607dfcc3 100644 --- a/trunk/src/utest/srs_utest_kernel.cpp +++ b/trunk/src/utest/srs_utest_kernel.cpp @@ -3843,7 +3843,11 @@ VOID TEST(KernelFileWriterTest, WriteSpecialCase) off_t seeked = 0; HELPER_EXPECT_SUCCESS(f.lseek(0, SEEK_CUR, &seeked)); +#ifdef SRS_AUTO_OSX + EXPECT_EQ(10, seeked); +#else EXPECT_EQ(0, seeked); +#endif } // Always fail. diff --git a/trunk/src/utest/srs_utest_service.cpp b/trunk/src/utest/srs_utest_service.cpp index 87a1c49d1..da8c8cec0 100644 --- a/trunk/src/utest/srs_utest_service.cpp +++ b/trunk/src/utest/srs_utest_service.cpp @@ -110,6 +110,7 @@ VOID TEST(TCPServerTest, PingPong) SrsTcpClient c(_srs_tmp_host, _srs_tmp_port, _srs_tmp_timeout); HELPER_EXPECT_SUCCESS(c.connect()); + srs_usleep(100 * SRS_UTIME_MILLISECONDS); EXPECT_TRUE(h.fd != NULL); } @@ -122,7 +123,10 @@ VOID TEST(TCPServerTest, PingPong) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); HELPER_EXPECT_SUCCESS(c.write((void*)"Hello", 5, NULL)); @@ -141,7 +145,10 @@ VOID TEST(TCPServerTest, PingPong) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); HELPER_EXPECT_SUCCESS(c.write((void*)"Hello", 5, NULL)); @@ -149,7 +156,7 @@ VOID TEST(TCPServerTest, PingPong) HELPER_EXPECT_SUCCESS(c.write((void*)"SRS", 3, NULL)); char buf[16] = {0}; - HELPER_EXPECT_SUCCESS(skt.read(buf, 9, NULL)); + HELPER_EXPECT_SUCCESS(skt.read_fully(buf, 9, NULL)); EXPECT_STREQ(buf, "Hello SRS"); } @@ -162,7 +169,10 @@ VOID TEST(TCPServerTest, PingPong) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); HELPER_EXPECT_SUCCESS(c.write((void*)"Hello SRS", 9, NULL)); @@ -194,7 +204,10 @@ VOID TEST(TCPServerTest, PingPongWithTimeout) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); skt.set_recv_timeout(1 * SRS_UTIME_MILLISECONDS); @@ -213,7 +226,10 @@ VOID TEST(TCPServerTest, PingPongWithTimeout) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); skt.set_recv_timeout(1 * SRS_UTIME_MILLISECONDS); @@ -232,7 +248,10 @@ VOID TEST(TCPServerTest, PingPongWithTimeout) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); skt.set_recv_timeout(1 * SRS_UTIME_MILLISECONDS); @@ -363,7 +382,9 @@ VOID TEST(TCPServerTest, StringIsHex) char* str = (char*)"!1234567890"; char* parsed = str; errno = 0; EXPECT_EQ(0x0, ::strtol(str, &parsed, 16)); +#ifndef SRS_AUTO_OSX EXPECT_EQ(0, errno); +#endif EXPECT_EQ(str, parsed); } @@ -379,7 +400,9 @@ VOID TEST(TCPServerTest, StringIsHex) char* str = (char*)""; char* parsed = str; errno = 0; EXPECT_EQ(0x0, ::strtol(str, &parsed, 16)); +#ifndef SRS_AUTO_OSX EXPECT_EQ(0, errno); +#endif EXPECT_EQ(str, parsed); } @@ -405,7 +428,10 @@ VOID TEST(TCPServerTest, WritevIOVC) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); iovec iovs[3]; @@ -432,7 +458,10 @@ VOID TEST(TCPServerTest, WritevIOVC) HELPER_EXPECT_SUCCESS(c.connect()); SrsStSocket skt; + srs_usleep(100 * SRS_UTIME_MILLISECONDS); +#ifdef SRS_AUTO_OSX ASSERT_TRUE(h.fd != NULL); +#endif HELPER_EXPECT_SUCCESS(skt.initialize(h.fd)); iovec iovs[3];