int SrsPlayEdge::on_ingest_play() { int ret = ERROR_SUCCESS; // when already connected(for instance, reconnect for error), ignore. if (state == SrsEdgeStateIngestConnected) { return ret; } srs_assert(state == SrsEdgeStatePlay); SrsEdgeState pstate = state; state = SrsEdgeStateIngestConnected; srs_trace("edge change from %d to state %d (ingest connected).", pstate, state); return ret; }
int SrsEdgeIngester::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 = SRS_CONSTS_RTMP_DEFAULT_PORT; int port = ::atoi(SRS_CONSTS_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. int64_t timeout = SRS_EDGE_INGESTER_TIMEOUT_US; if ((ret = srs_socket_connect(server, port, timeout, &stfd)) != ERROR_SUCCESS) { srs_warn("edge pull failed, stream=%s, tcUrl=%s to server=%s, port=%d, timeout=%"PRId64", ret=%d", _req->stream.c_str(), _req->tcUrl.c_str(), server.c_str(), port, timeout, ret); return ret; } srs_freep(client); srs_freep(io); srs_assert(stfd); io = new SrsStSocket(stfd); client = new SrsRtmpClient(io); kbps->set_io(io, io); srs_trace("edge pull connected, can_publish=%d, url=%s/%s, server=%s:%d", _source->can_publish(), _req->tcUrl.c_str(), _req->stream.c_str(), server.c_str(), port); return ret; }
int SrsEdgeIngester::ingest() { int ret = ERROR_SUCCESS; client->set_recv_timeout(SRS_EDGE_INGESTER_TIMEOUT_US); SrsPithyPrint pithy_print(SRS_CONSTS_STAGE_EDGE); while (pthread->can_loop()) { // switch to other st-threads. st_usleep(0); pithy_print.elapse(); // pithy print if (pithy_print.can_print()) { kbps->sample(); srs_trace("<- "SRS_CONSTS_LOG_EDGE_PLAY " time=%"PRId64", okbps=%d,%d,%d, ikbps=%d,%d,%d", pithy_print.age(), kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(), kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m()); } // read from client. SrsMessage* msg = NULL; if ((ret = client->recv_message(&msg)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("pull origin server message failed. ret=%d", ret); } return ret; } srs_verbose("edge loop recv message. ret=%d", ret); srs_assert(msg); SrsAutoFree(SrsMessage, msg); if ((ret = process_publish_message(msg)) != ERROR_SUCCESS) { return ret; } } return ret; }
void srs_random_generate(char* bytes, int size) { static bool _random_initialized = false; if (!_random_initialized) { srand(0); _random_initialized = true; srs_trace("srand initialized the random."); } static char cdata[] = { 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72, 0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x40, 0x31, 0x32, 0x36, 0x2e, 0x63, 0x6f, 0x6d }; for (int i = 0; i < size; i++) { bytes[i] = cdata[rand() % (sizeof(cdata) - 1)]; } }
int SrsSignalManager::start() { int ret = ERROR_SUCCESS; /** * Note that if multiple processes are used (see below), * the signal pipe should be initialized after the fork(2) call * so that each process has its own private pipe. */ struct sigaction sa; /* Create signal pipe */ if (pipe(sig_pipe) < 0) { ret = ERROR_SYSTEM_CREATE_PIPE; srs_error("create signal manager pipe failed. ret=%d", ret); return ret; } /* Install sig_catcher() as a signal handler */ sa.sa_handler = SrsSignalManager::sig_catcher; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGNAL_RELOAD, &sa, NULL); sa.sa_handler = SrsSignalManager::sig_catcher; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGTERM, &sa, NULL); sa.sa_handler = SrsSignalManager::sig_catcher; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGINT, &sa, NULL); sa.sa_handler = SrsSignalManager::sig_catcher; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; sigaction(SIGUSR2, &sa, NULL); srs_trace("signal installed"); return pthread->start(); }
int SrsServer::cycle() { int ret = ERROR_SUCCESS; // the deamon thread, update the time cache while (true) { st_usleep(SRS_TIME_RESOLUTION_MS * 1000); srs_update_system_time_ms(); // 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_GPERF_MC if (signal_gmc_stop) { break; } #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."); } } #ifdef SRS_INGEST ingester->stop(); #endif return ret; }
int srs_rtmp_expect_message(SrsProtocol* protocol, SrsCommonMessage** pmsg, T** ppacket) { *pmsg = NULL; *ppacket = NULL; int ret = ERROR_SUCCESS; while (true) { SrsCommonMessage* msg = NULL; if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { srs_error("recv message failed. ret=%d", ret); return ret; } srs_verbose("recv message success."); if ((ret = msg->decode_packet(protocol)) != ERROR_SUCCESS) { delete msg; srs_error("decode message failed. ret=%d", ret); return ret; } T* pkt = dynamic_cast<T*>(msg->get_packet()); if (!pkt) { delete msg; srs_trace("drop message(type=%d, size=%d, time=%"PRId64", sid=%d).", msg->header.message_type, msg->header.payload_length, msg->header.timestamp, msg->header.stream_id); continue; } *pmsg = msg; *ppacket = pkt; break; } 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 SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) { int ret = ERROR_SUCCESS; #ifdef SRS_HLS if ((ret = hls->on_meta_data(metadata)) != ERROR_SUCCESS) { srs_error("hls process onMetaData message failed. ret=%d", ret); return ret; } #endif metadata->metadata->set("server", new SrsAmf0String( RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); SrsAmf0Any* prop = NULL; if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) { if (prop->is_number()) { sample_rate = (int)(srs_amf0_convert<SrsAmf0Number>(prop)->value); } } if ((prop = metadata->metadata->get_property("framerate")) != NULL) { if (prop->is_number()) { frame_rate = (int)(srs_amf0_convert<SrsAmf0Number>(prop)->value); } } // encode the metadata to payload int size = metadata->get_payload_length(); if (size <= 0) { srs_warn("ignore the invalid metadata. size=%d", size); return ret; } srs_verbose("get metadata size success."); char* payload = new char[size]; memset(payload, 0, size); if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { srs_error("encode metadata error. ret=%d", ret); srs_freepa(payload); return ret; } srs_verbose("encode metadata success."); // create a shared ptr message. srs_freep(cache_metadata); cache_metadata = new SrsSharedPtrMessage(); // dump message to shared ptr message. if ((ret = cache_metadata->initialize(msg, payload, size)) != ERROR_SUCCESS) { srs_error("initialize the cache metadata failed. ret=%d", ret); return ret; } srs_verbose("initialize shared ptr metadata success."); // copy to all consumer if (true) { std::vector<SrsConsumer*>::iterator it; for (it = consumers.begin(); it != consumers.end(); ++it) { SrsConsumer* consumer = *it; if ((ret = consumer->enqueue(cache_metadata->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { srs_error("dispatch the metadata failed. ret=%d", ret); return ret; } } srs_trace("dispatch metadata success."); } // copy to all forwarders if (true) { std::vector<SrsForwarder*>::iterator it; for (it = forwarders.begin(); it != forwarders.end(); ++it) { SrsForwarder* forwarder = *it; if ((ret = forwarder->on_meta_data(cache_metadata->copy())) != ERROR_SUCCESS) { srs_error("forwarder process onMetaData message failed. ret=%d", ret); return ret; } } } return ret; }
int main(int argc, char** argv) { srs_trace("tcp server to send random data to clients."); if (argc <= 2) { srs_trace("Usage: %s <port> <packet_bytes>", argv[0]); srs_trace(" port: the listen port."); srs_trace(" packet_bytes: the bytes for packet to send."); srs_trace("For example:"); srs_trace(" %s %d %d", argv[0], 1990, 4096); return -1; } int listen_port = ::atoi(argv[1]); int packet_bytes = ::atoi(argv[2]); srs_trace("listen_port is %d", listen_port); srs_trace("packet_bytes is %d", packet_bytes); int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { srs_trace("create socket failed."); return -1; } 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("setsockopt reuse-addr success. fd=%d", fd); sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(listen_port); addr.sin_addr.s_addr = INADDR_ANY; if (::bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { srs_trace("bind socket error."); return -1; } srs_trace("bind socket success. fd=%d", fd); if (::listen(fd, 10) == -1) { srs_trace("listen socket error."); return -1; } srs_trace("listen socket success. fd=%d", fd); // get the sockoptions int sock_send_buffer = 0; socklen_t nb_sock_send_buffer = sizeof(int); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sock_send_buffer, &nb_sock_send_buffer) < 0) { srs_trace("get sockopt failed."); return -1; } srs_trace("SO_SNDBUF=%d", sock_send_buffer); for (;;) { int conn = accept(fd, NULL, NULL); if (conn < 0) { srs_trace("accept socket error."); return -1; } srs_trace("accept socket ok, conn=%d", conn); char b[4096]; for (;;) { ssize_t nb_send = send(conn, b, sizeof(b), 0); if (nb_send != sizeof(b)) { srs_trace("send bytes to socket error."); ::close(conn); break; } } } return 0; }
void SrsListener::on_thread_start() { srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd); }
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 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()); return ret; }
int SrsForwarder::forward() { int ret = ERROR_SUCCESS; client->set_recv_timeout(SRS_PULSE_TIMEOUT_US); SrsPithyPrint pithy_print(SRS_STAGE_FORWARDER); SrsSharedPtrMessageArray msgs(SYS_MAX_FORWARD_SEND_MSGS); 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; if ((ret = queue->dump_packets(msgs.size, msgs.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_30s(), kbps->get_send_kbps_5m(), kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m()); } // ignore when no messages. if (count <= 0) { srs_verbose("no packets to forward."); continue; } // 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.msgs[i]; srs_assert(msg); msgs.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 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; }
/** * main entrance. */ int main(int argc, char** argv) { int ret = ERROR_SUCCESS; // TODO: support both little and big endian. srs_assert(srs_is_little_endian()); #ifdef SRS_AUTO_GPERF_MP HeapProfilerStart("gperf.srs.gmp"); #endif #ifdef SRS_AUTO_GPERF_CP ProfilerStart("gperf.srs.gcp"); #endif #if defined(SRS_AUTO_GPERF_MC) && defined(SRS_AUTO_GPERF_MP) srs_error("option --with-gmc confict with --with-gmp, " "@see: http://google-perftools.googlecode.com/svn/trunk/doc/heap_checker.html\n" "Note that since the heap-checker uses the heap-profiling framework internally, " "it is not possible to run both the heap-checker and heap profiler at the same time"); return -1; #endif // never use srs log(srs_trace, srs_error, etc) before config parse the option, // which will load the log config and apply it. if ((ret = _srs_config->parse_options(argc, argv)) != ERROR_SUCCESS) { return ret; } // config parsed, initialize log. if ((ret = _srs_log->initialize()) != ERROR_SUCCESS) { return ret; } // we check the config when the log initialized. if ((ret = _srs_config->check_config()) != ERROR_SUCCESS) { return ret; } srs_trace("srs(simple-rtmp-server) "RTMP_SIG_SRS_VERSION); srs_trace("license: "RTMP_SIG_SRS_LICENSE); srs_trace("authors: "RTMP_SIG_SRS_PRIMARY_AUTHROS); srs_trace("contributors: "SRS_AUTO_CONSTRIBUTORS); srs_trace("uname: "SRS_AUTO_UNAME); srs_trace("build: %s, %s", SRS_AUTO_BUILD_DATE, srs_is_little_endian()? "little-endian":"big-endian"); srs_trace("configure: "SRS_AUTO_USER_CONFIGURE); srs_trace("features: "SRS_AUTO_CONFIGURE); #ifdef SRS_AUTO_ARM_UBUNTU12 srs_trace("arm tool chain: "SRS_AUTO_EMBEDED_TOOL_CHAIN); #endif srs_trace("conf: %s, limit: %d", _srs_config->config().c_str(), _srs_config->get_max_connections()); // features show_macro_features(); check_macro_features(); /** * we do nothing in the constructor of server, * and use initialize to create members, set hooks for instance the reload handler, * all initialize will done in this stage. */ if ((ret = _srs_server->initialize()) != ERROR_SUCCESS) { return ret; } return run(); }
/** * show the features by macro, the actual macro values. */ void show_macro_features() { #ifdef SRS_AUTO_SSL srs_trace("check feature rtmp handshake: on"); #else srs_warn("check feature rtmp handshake: off"); #endif #ifdef SRS_AUTO_HLS srs_trace("check feature hls: on"); #else srs_warn("check feature hls: off"); #endif #ifdef SRS_AUTO_HTTP_CALLBACK srs_trace("check feature http callback: on"); #else srs_warn("check feature http callback: off"); #endif #ifdef SRS_AUTO_HTTP_API srs_trace("check feature http api: on"); #else srs_warn("check feature http api: off"); #endif #ifdef SRS_AUTO_HTTP_SERVER srs_trace("check feature http server: on"); #else srs_warn("check feature http server: off"); #endif #ifdef SRS_AUTO_HTTP_PARSER srs_trace("check feature http parser: on"); #else srs_warn("check feature http parser: off"); #endif #ifdef SRS_AUTO_DVR srs_trace("check feature dvr: on"); #else srs_warn("check feature dvr: off"); #endif #ifdef SRS_AUTO_TRANSCODE srs_trace("check feature transcode: on"); #else srs_warn("check feature transcode: off"); #endif #ifdef SRS_AUTO_INGEST srs_trace("check feature ingest: on"); #else srs_warn("check feature ingest: off"); #endif #ifdef SRS_AUTO_STAT srs_trace("check feature system stat: on"); #else srs_warn("check feature system stat: off"); #endif #ifdef SRS_AUTO_NGINX srs_trace("check feature compile nginx: on"); #else srs_warn("check feature compile nginx: off"); #endif #ifdef SRS_AUTO_FFMPEG_TOOL srs_trace("check feature compile ffmpeg: on"); #else srs_warn("check feature compile ffmpeg: off"); #endif }
int SrsSource::on_video(SrsCommonMessage* video) { int ret = ERROR_SUCCESS; SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); SrsAutoFree(SrsSharedPtrMessage, msg, false); if ((ret = msg->initialize(video)) != ERROR_SUCCESS) { srs_error("initialize the video failed. ret=%d", ret); return ret; } srs_verbose("initialize shared ptr video success."); #ifdef SRS_HLS if ((ret = hls->on_video(msg->copy())) != ERROR_SUCCESS) { srs_error("hls process video message failed. ret=%d", ret); return ret; } #endif // copy to all consumer if (true) { std::vector<SrsConsumer*>::iterator it; for (it = consumers.begin(); it != consumers.end(); ++it) { SrsConsumer* consumer = *it; if ((ret = consumer->enqueue(msg->copy(), sample_rate, frame_rate)) != ERROR_SUCCESS) { srs_error("dispatch the video failed. ret=%d", ret); return ret; } } srs_info("dispatch video success."); } // copy to all forwarders. if (true) { std::vector<SrsForwarder*>::iterator it; for (it = forwarders.begin(); it != forwarders.end(); ++it) { SrsForwarder* forwarder = *it; if ((ret = forwarder->on_video(msg->copy())) != ERROR_SUCCESS) { srs_error("forwarder process video message failed. ret=%d", ret); return ret; } } } // cache the sequence header if h264 if (SrsCodec::video_is_sequence_header(msg->payload, msg->size)) { srs_freep(cache_sh_video); cache_sh_video = msg->copy(); srs_trace("update video sequence header success. size=%d", msg->header.payload_length); return ret; } // cache the last gop packets if ((ret = gop_cache->cache(msg)) != ERROR_SUCCESS) { srs_error("shrink gop cache failed. ret=%d", ret); return ret; } srs_verbose("cache gop success."); return ret; }
int SrsFFMPEG::start() { int ret = ERROR_SUCCESS; if (started) { return ret; } // prepare exec params char tmp[256]; std::vector<std::string> params; // argv[0], set to ffmpeg bin. // The execv() and execvp() functions .... // The first argument, by convention, should point to // the filename associated with the file being executed. params.push_back(ffmpeg); // input. params.push_back("-f"); params.push_back("flv"); params.push_back("-i"); params.push_back(input); // build the filter if (!vfilter.empty()) { std::vector<std::string>::iterator it; for (it = vfilter.begin(); it != vfilter.end(); ++it) { std::string p = *it; if (!p.empty()) { params.push_back(p); } } } // video specified. params.push_back("-vcodec"); params.push_back(vcodec); params.push_back("-b:v"); snprintf(tmp, sizeof(tmp), "%d", vbitrate * 1000); params.push_back(tmp); params.push_back("-r"); snprintf(tmp, sizeof(tmp), "%.2f", vfps); params.push_back(tmp); params.push_back("-s"); snprintf(tmp, sizeof(tmp), "%dx%d", vwidth, vheight); params.push_back(tmp); // TODO: add aspect if needed. params.push_back("-aspect"); snprintf(tmp, sizeof(tmp), "%d:%d", vwidth, vheight); params.push_back(tmp); params.push_back("-threads"); snprintf(tmp, sizeof(tmp), "%d", vthreads); params.push_back(tmp); params.push_back("-profile:v"); params.push_back(vprofile); params.push_back("-preset"); params.push_back(vpreset); // vparams if (!vparams.empty()) { std::vector<std::string>::iterator it; for (it = vparams.begin(); it != vparams.end(); ++it) { std::string p = *it; if (!p.empty()) { params.push_back(p); } } } // audio specified. params.push_back("-acodec"); params.push_back(acodec); params.push_back("-b:a"); snprintf(tmp, sizeof(tmp), "%d", abitrate * 1000); params.push_back(tmp); params.push_back("-ar"); snprintf(tmp, sizeof(tmp), "%d", asample_rate); params.push_back(tmp); params.push_back("-ac"); snprintf(tmp, sizeof(tmp), "%d", achannels); params.push_back(tmp); // aparams if (!aparams.empty()) { std::vector<std::string>::iterator it; for (it = aparams.begin(); it != aparams.end(); ++it) { std::string p = *it; if (!p.empty()) { params.push_back(p); } } } // output params.push_back("-f"); params.push_back("flv"); params.push_back("-y"); params.push_back(output); // TODO: fork or vfork? if ((pid = fork()) < 0) { ret = ERROR_ENCODER_FORK; srs_error("vfork process failed. ret=%d", ret); return ret; } // child process: ffmpeg encoder engine. if (pid == 0) { // memory leak in child process, it's ok. char** charpv_params = new char*[params.size() + 1]; for (int i = 0; i < (int)params.size(); i++) { std::string p = params[i]; charpv_params[i] = (char*)p.c_str(); } // EOF: NULL charpv_params[params.size()] = NULL; // TODO: execv or execvp ret = execv(ffmpeg.c_str(), charpv_params); if (ret < 0) { fprintf(stderr, "fork ffmpeg failed, errno=%d(%s)", errno, strerror(errno)); } exit(ret); } // parent. if (pid > 0) { started = true; srs_trace("vfored ffmpeg encoder engine, pid=%d", pid); return ret; } return ret; }
void handler(int signo) { srs_trace("get a signal, signo=%d", signo); _srs_server->on_signal(signo); }
int main(int argc, char** argv) { srs_trace("tcp server to send random data to clients."); if (argc <= 3) { srs_trace("Usage: %s <nb_writev> <port> <packet_bytes>", argv[0]); srs_trace(" nb_writev: the number of iovec for writev."); srs_trace(" port: the listen port."); srs_trace(" packet_bytes: the bytes for packet to send."); srs_trace("For example:"); srs_trace(" %s %d %d", argv[0], 64, 1990, 4096); return -1; } int nb_writev = ::atoi(argv[1]); int listen_port = ::atoi(argv[2]); int packet_bytes = ::atoi(argv[3]); srs_trace("nb_writev is %d", nb_writev); srs_trace("listen_port is %d", listen_port); srs_trace("packet_bytes is %d", packet_bytes); int fd = socket(AF_INET, SOCK_STREAM, 0); if (fd < 0) { srs_trace("create socket failed."); return -1; } 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("setsockopt reuse-addr success. fd=%d", fd); sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = htons(listen_port); addr.sin_addr.s_addr = INADDR_ANY; if (::bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { srs_trace("bind socket error."); return -1; } srs_trace("bind socket success. fd=%d", fd); if (::listen(fd, 10) == -1) { srs_trace("listen socket error."); return -1; } srs_trace("listen socket success. fd=%d", fd); // get the sockoptions int sock_send_buffer = 0; socklen_t nb_sock_send_buffer = sizeof(int); if (getsockopt(fd, SOL_SOCKET, SO_SNDBUF, &sock_send_buffer, &nb_sock_send_buffer) < 0) { srs_trace("get sockopt failed."); return -1; } srs_trace("SO_SNDBUF=%d", sock_send_buffer); iovec* iov = new iovec[nb_writev]; for (int i = 0; i < nb_writev; i++) { iovec& item = iov[i]; item.iov_base = new char[packet_bytes]; item.iov_len = packet_bytes; } for (;;) { int conn = accept(fd, NULL, NULL); if (conn < 0) { srs_trace("accept socket error."); return -1; } srs_trace("accept socket ok, conn=%d", conn); for (;;) { ssize_t nb_send = writev(conn, iov, nb_writev); if (nb_send != packet_bytes * nb_writev) { srs_trace("send bytes to socket error."); ::close(conn); break; } } } return 0; }
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); kbps->set_io(io, 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), SRS_EDGE_FORWARDER_TIMEOUT_US) == -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()) { kbps->sample(); srs_trace("-> "SRS_LOG_ID_EDGE_PUBLISH " 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 to origin. // @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("edge publish forwarder send message to server failed. ret=%d", ret); return ret; } } } 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); // read from client. if (true) { SrsCommonMessage* 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; } } // 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; } // ignore when no messages. if (count <= 0) { srs_verbose("no packets to forward."); continue; } SrsAutoFree(SrsSharedPtrMessage*, msgs, true); // pithy print pithy_print.elapse(SRS_PULSE_TIMEOUT_US / 1000); if (pithy_print.can_print()) { srs_trace("-> time=%"PRId64", msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", pithy_print.get_age(), count, client->get_send_bytes(), client->get_recv_bytes(), client->get_send_kbps(), client->get_recv_kbps()); } // 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_message(msg)) != ERROR_SUCCESS) { srs_error("forwarder send message to server failed. ret=%d", ret); return ret; } } } return ret; }