int SrsForwarder::on_publish(SrsRequest* req, std::string forward_server) { int ret = ERROR_SUCCESS; // forward app app = req->app; stream_name = req->stream; server = forward_server; std::string s_port = RTMP_DEFAULT_PORT; port = ::atoi(RTMP_DEFAULT_PORT); // TODO: FIXME: parse complex params size_t pos = forward_server.find(":"); if (pos != std::string::npos) { s_port = forward_server.substr(pos + 1); server = forward_server.substr(0, pos); } // discovery vhost std::string vhost = req->vhost; srs_vhost_resolve(vhost, s_port); port = ::atoi(s_port.c_str()); // generate tcUrl tc_url = "rtmp://"; if (vhost == RTMP_VHOST_DEFAULT) { tc_url += forward_server; } else { tc_url += vhost; } tc_url += "/"; tc_url += req->app; // dead loop check std::string source_ep = "rtmp://"; source_ep += req->host; source_ep += ":"; source_ep += req->port; source_ep += "?vhost="; source_ep += req->vhost; std::string dest_ep = "rtmp://"; if (forward_server == "127.0.0.1") { dest_ep += req->host; } else { dest_ep += forward_server; } dest_ep += ":"; dest_ep += s_port; dest_ep += "?vhost="; dest_ep += vhost; if (source_ep == dest_ep) { ret = ERROR_SYSTEM_FORWARD_LOOP; srs_warn("forward loop detected. src=%s, dest=%s, ret=%d", source_ep.c_str(), dest_ep.c_str(), ret); return ret; } srs_trace("start forward %s to %s, tcUrl=%s, stream=%s", source_ep.c_str(), dest_ep.c_str(), tc_url.c_str(), stream_name.c_str()); if ((ret = pthread->start()) != ERROR_SUCCESS) { srs_error("start srs thread failed. ret=%d", ret); return ret; } srs_trace("forward thread cid=%d, current_cid=%d", pthread->cid(), _srs_context->get_id()); return ret; }
int c1s1::s1_create(c1s1* c1) { int ret = ERROR_SUCCESS; if (c1->schema == srs_schema_invalid) { ret = ERROR_RTMP_CH_SCHEMA; srs_error("create s1 failed. invalid schema=%d, ret=%d", c1->schema, ret); return ret; } destroy_blocks(); schema = c1->schema; time = ::time(NULL); version = 0x01000504; // server s1 version SrsDH dh; // ensure generate 128bytes public key. if ((ret = dh.initialize(true)) != ERROR_SUCCESS) { return ret; } if (schema == srs_schema0) { srs_key_block_init(&block0.key); srs_digest_block_init(&block1.digest); // directly generate the public key. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 int pkey_size = 128; if ((ret = dh.copy_public_key((char*)block0.key.key, pkey_size)) != ERROR_SUCCESS) { srs_error("calc s1 key failed. ret=%d", ret); return ret; } srs_assert(pkey_size == 128); } else { srs_digest_block_init(&block0.digest); srs_key_block_init(&block1.key); // directly generate the public key. // @see: https://github.com/winlinvip/simple-rtmp-server/issues/148 int pkey_size = 128; if ((ret = dh.copy_public_key((char*)block1.key.key, pkey_size)) != ERROR_SUCCESS) { srs_error("calc s1 key failed. ret=%d", ret); return ret; } srs_assert(pkey_size == 128); } srs_verbose("calc s1 key success."); char* s1_digest = NULL; if ((ret = calc_s1_digest(s1_digest)) != ERROR_SUCCESS) { srs_error("calc s1 digest failed. ret=%d", ret); return ret; } srs_verbose("calc s1 digest success."); srs_assert(s1_digest != NULL); SrsAutoFree(char, s1_digest); if (schema == srs_schema0) { memcpy(block1.digest.digest, s1_digest, 32); } else { memcpy(block0.digest.digest, s1_digest, 32); } srs_verbose("copy s1 key success."); return ret; }
int SrsForwarder::forward() { int ret = ERROR_SUCCESS; client->set_recv_timeout(SRS_PULSE_TIMEOUT_US); SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER); while (pthread->can_loop()) { // switch to other st-threads. st_usleep(0); pithy_print.elapse(); // read from client. if (true) { SrsMessage* msg = NULL; ret = client->recv_message(&msg); srs_verbose("play loop recv message. ret=%d", ret); if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { srs_error("recv server control message failed. ret=%d", ret); return ret; } srs_freep(msg); } // forward all messages. int count = 0; SrsSharedPtrMessage** msgs = NULL; if ((ret = queue->get_packets(0, msgs, count)) != ERROR_SUCCESS) { srs_error("get message to forward failed. ret=%d", ret); return ret; } // pithy print if (pithy_print.can_print()) { kbps->sample(); srs_trace("-> "SRS_LOG_ID_FOWARDER " time=%"PRId64", msgs=%d, okbps=%d,%d,%d, ikbps=%d,%d,%d", pithy_print.age(), count, kbps->get_send_kbps(), kbps->get_send_kbps_sample_high(), kbps->get_send_kbps_sample_medium(), kbps->get_recv_kbps(), kbps->get_recv_kbps_sample_high(), kbps->get_recv_kbps_sample_medium()); } // ignore when no messages. if (count <= 0) { srs_verbose("no packets to forward."); continue; } SrsAutoFreeArray(SrsSharedPtrMessage, msgs, count); // all msgs to forward. // @remark, becareful, all msgs must be free explicitly, // free by send_and_free_message or srs_freep. for (int i = 0; i < count; i++) { SrsSharedPtrMessage* msg = msgs[i]; srs_assert(msg); msgs[i] = NULL; if ((ret = client->send_and_free_message(msg, stream_id)) != ERROR_SUCCESS) { srs_error("forwarder send message to server failed. ret=%d", ret); return ret; } } } return ret; }
int SrsEdgeForwarder::connect_server() { int ret = ERROR_SUCCESS; // reopen close_underlayer_socket(); SrsConfDirective* conf = _srs_config->get_vhost_edge_origin(_req->vhost); srs_assert(conf); // select the origin. std::string server = conf->args.at(origin_index % conf->args.size()); origin_index = (origin_index + 1) % conf->args.size(); std::string s_port = RTMP_DEFAULT_PORT; int port = ::atoi(RTMP_DEFAULT_PORT); size_t pos = server.find(":"); if (pos != std::string::npos) { s_port = server.substr(pos + 1); server = server.substr(0, pos); port = ::atoi(s_port.c_str()); } // open socket. srs_trace("connect edge stream=%s, tcUrl=%s to server=%s, port=%d", _req->stream.c_str(), _req->tcUrl.c_str(), server.c_str(), port); // TODO: FIXME: extract utility method int sock = socket(AF_INET, SOCK_STREAM, 0); if(sock == -1){ ret = ERROR_SOCKET_CREATE; srs_error("create socket error. ret=%d", ret); return ret; } srs_assert(!stfd); stfd = st_netfd_open_socket(sock); if(stfd == NULL){ ret = ERROR_ST_OPEN_SOCKET; srs_error("st_netfd_open_socket failed. ret=%d", ret); return ret; } srs_freep(client); srs_freep(io); io = new SrsSocket(stfd); client = new SrsRtmpClient(io); // connect to server. std::string ip = srs_dns_resolve(server); if (ip.empty()) { ret = ERROR_SYSTEM_IP_INVALID; srs_error("dns resolve server error, ip empty. ret=%d", ret); return ret; } sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(port); addr.sin_addr.s_addr = inet_addr(ip.c_str()); if (st_connect(stfd, (const struct sockaddr*)&addr, sizeof(sockaddr_in), ST_UTIME_NO_TIMEOUT) == -1){ ret = ERROR_ST_CONNECT; srs_error("connect to server error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret); return ret; } srs_trace("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port); return ret; }
int SrsEdgeForwarder::cycle() { int ret = ERROR_SUCCESS; client->set_recv_timeout(SRS_PULSE_TIMEOUT_US); SrsPithyPrint pithy_print(SRS_STAGE_EDGE); while (pthread->can_loop()) { // switch to other st-threads. st_usleep(0); if (send_error_code != ERROR_SUCCESS) { st_usleep(SRS_EDGE_FORWARDER_ERROR_US); continue; } // read from client. if (true) { SrsMessage* msg = NULL; ret = client->recv_message(&msg); srs_verbose("edge loop recv message. ret=%d", ret); if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { srs_error("edge forwarder recv server control message failed. ret=%d", ret); send_error_code = ret; continue; } srs_freep(msg); } // forward all messages. int count = 0; SrsSharedPtrMessage** msgs = NULL; if ((ret = queue->get_packets(0, msgs, count)) != ERROR_SUCCESS) { srs_error("get message to forward to origin failed. ret=%d", ret); return ret; } pithy_print.elapse(); // pithy print if (pithy_print.can_print()) { srs_trace("-> "SRS_LOG_ID_EDGE_PUBLISH " time=%"PRId64", msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", pithy_print.age(), count, client->get_send_bytes(), client->get_recv_bytes(), client->get_send_kbps(), client->get_recv_kbps()); } // ignore when no messages. if (count <= 0) { srs_verbose("no packets to forward."); continue; } SrsAutoFree(SrsSharedPtrMessage*, msgs, true); // all msgs to forward. for (int i = 0; i < count; i++) { SrsSharedPtrMessage* msg = msgs[i]; srs_assert(msg); msgs[i] = NULL; if ((ret = client->send_and_free_message(msg)) != ERROR_SUCCESS) { srs_error("edge publish forwarder send message to server failed. ret=%d", ret); return ret; } } } return ret; }
int SrsServer::do_cycle() { int ret = ERROR_SUCCESS; // find the max loop int max = srs_max(0, SRS_SYS_TIME_RESOLUTION_MS_TIMES); max = srs_max(max, SRS_SYS_RUSAGE_RESOLUTION_TIMES); max = srs_max(max, SRS_SYS_CPU_STAT_RESOLUTION_TIMES); max = srs_max(max, SRS_SYS_DISK_STAT_RESOLUTION_TIMES); max = srs_max(max, SRS_SYS_MEMINFO_RESOLUTION_TIMES); max = srs_max(max, SRS_SYS_PLATFORM_INFO_RESOLUTION_TIMES); max = srs_max(max, SRS_SYS_NETWORK_DEVICE_RESOLUTION_TIMES); max = srs_max(max, SRS_SYS_NETWORK_RTMP_SERVER_RESOLUTION_TIMES); // the deamon thread, update the time cache while (true) { // the interval in config. int heartbeat_max_resolution = (int)(_srs_config->get_heartbeat_interval() / 100); // dynamic fetch the max. int __max = max; __max = srs_max(__max, heartbeat_max_resolution); for (int i = 0; i < __max; i++) { st_usleep(SRS_SYS_CYCLE_INTERVAL * 1000); // for gperf heap checker, // @see: research/gperftools/heap-checker/heap_checker.cc // if user interrupt the program, exit to check mem leak. // but, if gperf, use reload to ensure main return normally, // because directly exit will cause core-dump. #ifdef SRS_AUTO_GPERF_MC if (signal_gmc_stop) { srs_warn("gmc got singal to stop server."); return ret; } #endif if (signal_reload) { signal_reload = false; srs_info("get signal reload, to reload the config."); if ((ret = _srs_config->reload()) != ERROR_SUCCESS) { srs_error("reload config failed. ret=%d", ret); return ret; } srs_trace("reload config success."); } // update the cache time or rusage. if ((i % SRS_SYS_TIME_RESOLUTION_MS_TIMES) == 0) { srs_info("update current time cache."); srs_update_system_time_ms(); } if ((i % SRS_SYS_RUSAGE_RESOLUTION_TIMES) == 0) { srs_info("update resource info, rss."); srs_update_system_rusage(); } if ((i % SRS_SYS_CPU_STAT_RESOLUTION_TIMES) == 0) { srs_info("update cpu info, cpu usage."); srs_update_proc_stat(); } if ((i % SRS_SYS_DISK_STAT_RESOLUTION_TIMES) == 0) { srs_info("update disk info, disk iops."); srs_update_disk_stat(); } if ((i % SRS_SYS_MEMINFO_RESOLUTION_TIMES) == 0) { srs_info("update memory info, usage/free."); srs_update_meminfo(); } if ((i % SRS_SYS_PLATFORM_INFO_RESOLUTION_TIMES) == 0) { srs_info("update platform info, uptime/load."); srs_update_platform_info(); } if ((i % SRS_SYS_NETWORK_DEVICE_RESOLUTION_TIMES) == 0) { srs_info("update network devices info."); srs_update_network_devices(); } if ((i % SRS_SYS_NETWORK_RTMP_SERVER_RESOLUTION_TIMES) == 0) { srs_info("update network rtmp server info."); resample_kbps(NULL); srs_update_rtmp_server((int)conns.size(), kbps); } #ifdef SRS_AUTO_HTTP_PARSER if (_srs_config->get_heartbeat_enabled()) { if ((i % heartbeat_max_resolution) == 0) { srs_info("do http heartbeat, for internal server to report."); http_heartbeat->heartbeat(); } } #endif srs_info("server main thread loop"); } } return ret; }
int SrsServer::acquire_pid_file() { int ret = ERROR_SUCCESS; std::string pid_file = _srs_config->get_pid_file(); // -rw-r--r-- // 644 int mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; int fd; // open pid file if ((fd = ::open(pid_file.c_str(), O_WRONLY | O_CREAT, mode)) < 0) { ret = ERROR_SYSTEM_PID_ACQUIRE; srs_error("open pid file %s error, ret=%#x", pid_file.c_str(), ret); return ret; } // require write lock struct flock lock; lock.l_type = F_WRLCK; // F_RDLCK, F_WRLCK, F_UNLCK lock.l_start = 0; // type offset, relative to l_whence lock.l_whence = SEEK_SET; // SEEK_SET, SEEK_CUR, SEEK_END lock.l_len = 0; if (fcntl(fd, F_SETLK, &lock) < 0) { if(errno == EACCES || errno == EAGAIN) { ret = ERROR_SYSTEM_PID_ALREADY_RUNNING; srs_error("srs is already running! ret=%#x", ret); return ret; } ret = ERROR_SYSTEM_PID_LOCK; srs_error("require lock for file %s error! ret=%#x", pid_file.c_str(), ret); return ret; } // truncate file if (ftruncate(fd, 0) < 0) { ret = ERROR_SYSTEM_PID_TRUNCATE_FILE; srs_error("truncate pid file %s error! ret=%#x", pid_file.c_str(), ret); return ret; } int pid = (int)getpid(); // write the pid char buf[512]; snprintf(buf, sizeof(buf), "%d", pid); if (write(fd, buf, strlen(buf)) != (int)strlen(buf)) { ret = ERROR_SYSTEM_PID_WRITE_FILE; srs_error("write our pid error! pid=%d file=%s ret=%#x", pid, pid_file.c_str(), ret); return ret; } // auto close when fork child process. int val; if ((val = fcntl(fd, F_GETFD, 0)) < 0) { ret = ERROR_SYSTEM_PID_GET_FILE_INFO; srs_error("fnctl F_GETFD error! file=%s ret=%#x", pid_file.c_str(), ret); return ret; } val |= FD_CLOEXEC; if (fcntl(fd, F_SETFD, val) < 0) { ret = ERROR_SYSTEM_PID_SET_FILE_INFO; srs_error("fcntl F_SETFD error! file=%s ret=%#x", pid_file.c_str(), ret); return ret; } srs_trace("write pid=%d to %s success!", pid, pid_file.c_str()); pid_fd = fd; return ret; }