int SrsGopCache::cache(SrsSharedPtrMessage* msg)
{
	int ret = ERROR_SUCCESS;
	
	if (!enable_gop_cache) {
		srs_verbose("gop cache is disabled.");
		return ret;
	}
	
	// got video, update the video count if acceptable
	if (msg->header.is_video()) {
		cached_video_count++;
	}
	
	// no acceptable video or pure audio, disable the cache.
	if (cached_video_count == 0) {
		srs_verbose("ignore any frame util got a h264 video frame.");
		return ret;
	}
	
	// clear gop cache when got key frame
	if (msg->header.is_video() && SrsCodec::video_is_keyframe(msg->payload, msg->size)) {
		srs_info("clear gop cache when got keyframe. vcount=%d, count=%d",
			cached_video_count, (int)gop_cache.size());
			
		clear();
		
		// curent msg is video frame, so we set to 1.
		cached_video_count = 1;
	}
	
	// cache the frame.
	gop_cache.push_back(msg->copy());
	
	return ret;
}
int SrsFFMPEG::initialize(std::string vhost, std::string port, std::string app, std::string stream, SrsConfDirective* engine)
{
	int ret = ERROR_SUCCESS;
	
	config->get_engine_vfilter(engine, vfilter);
	vcodec 			= config->get_engine_vcodec(engine);
	vbitrate 		= config->get_engine_vbitrate(engine);
	vfps 			= config->get_engine_vfps(engine);
	vwidth 			= config->get_engine_vwidth(engine);
	vheight 		= config->get_engine_vheight(engine);
	vthreads 		= config->get_engine_vthreads(engine);
	vprofile 		= config->get_engine_vprofile(engine);
	vpreset 		= config->get_engine_vpreset(engine);
	config->get_engine_vparams(engine, vparams);
	acodec 			= config->get_engine_acodec(engine);
	abitrate 		= config->get_engine_abitrate(engine);
	asample_rate 	= config->get_engine_asample_rate(engine);
	achannels 		= config->get_engine_achannels(engine);
	config->get_engine_aparams(engine, aparams);
	output 			= config->get_engine_output(engine);
	
	// ensure the size is even.
	vwidth -= vwidth % 2;
	vheight -= vheight % 2;

	// input stream, from local.
	// ie. rtmp://127.0.0.1:1935/live/livestream
	input = "rtmp://127.0.0.1:";
	input += port;
	input += "/";
	input += app;
	input += "/";
	input += stream;
	
	// output stream, to other/self server
	// ie. rtmp://127.0.0.1:1935/live/livestream_sd
	if (vhost == RTMP_VHOST_DEFAULT) {
		output = srs_replace(output, "[vhost]", "127.0.0.1");
	} else {
		output = srs_replace(output, "[vhost]", vhost);
	}
	output = srs_replace(output, "[port]", port);
	output = srs_replace(output, "[app]", app);
	output = srs_replace(output, "[stream]", stream);

	// important: loop check, donot transcode again.
	// we think the following is loop circle:
	// input: rtmp://127.0.0.1:1935/live/livestream_sd
	// output: rtmp://127.0.0.1:1935/live/livestream_sd_sd
	std::string tail = ""; // tail="_sd"
	if (output.length() > input.length()) {
		tail = output.substr(input.length());
	}
	// TODO: better dead loop check. 
	// if input also endwiths the tail, loop detected.
	if (!tail.empty() && input.rfind(tail) == input.length() - tail.length()) {
		ret = ERROR_ENCODER_LOOP;
		srs_info("detect a loop cycle, input=%s, output=%s, ignore it. ret=%d",
			input.c_str(), output.c_str(), ret);
		return ret;
	}
	
	if (vcodec != SRS_ENCODER_VCODEC) {
		ret = ERROR_ENCODER_VCODEC;
		srs_error("invalid vcodec, must be %s, actual %s, ret=%d",
			SRS_ENCODER_VCODEC, vcodec.c_str(), ret);
		return ret;
	}
	if (vbitrate <= 0) {
		ret = ERROR_ENCODER_VBITRATE;
		srs_error("invalid vbitrate: %d, ret=%d", vbitrate, ret);
		return ret;
	}
	if (vfps <= 0) {
		ret = ERROR_ENCODER_VFPS;
		srs_error("invalid vfps: %.2f, ret=%d", vfps, ret);
		return ret;
	}
	if (vwidth <= 0) {
		ret = ERROR_ENCODER_VWIDTH;
		srs_error("invalid vwidth: %d, ret=%d", vwidth, ret);
		return ret;
	}
	if (vheight <= 0) {
		ret = ERROR_ENCODER_VHEIGHT;
		srs_error("invalid vheight: %d, ret=%d", vheight, ret);
		return ret;
	}
	if (vthreads < 0) {
		ret = ERROR_ENCODER_VTHREADS;
		srs_error("invalid vthreads: %d, ret=%d", vthreads, ret);
		return ret;
	}
	if (vprofile.empty()) {
		ret = ERROR_ENCODER_VPROFILE;
		srs_error("invalid vprofile: %s, ret=%d", vprofile.c_str(), ret);
		return ret;
	}
	if (vpreset.empty()) {
		ret = ERROR_ENCODER_VPRESET;
		srs_error("invalid vpreset: %s, ret=%d", vpreset.c_str(), ret);
		return ret;
	}
	if (acodec != SRS_ENCODER_ACODEC) {
		ret = ERROR_ENCODER_ACODEC;
		srs_error("invalid acodec, must be %s, actual %s, ret=%d",
			SRS_ENCODER_ACODEC, acodec.c_str(), ret);
		return ret;
	}
	if (abitrate <= 0) {
		ret = ERROR_ENCODER_ABITRATE;
		srs_error("invalid abitrate: %d, ret=%d", 
			abitrate, ret);
		return ret;
	}
	if (asample_rate <= 0) {
		ret = ERROR_ENCODER_ASAMPLE_RATE;
		srs_error("invalid sample rate: %d, ret=%d", 
			asample_rate, ret);
		return ret;
	}
	if (achannels != 1 && achannels != 2) {
		ret = ERROR_ENCODER_ACHANNELS;
		srs_error("invalid achannels, must be 1 or 2, actual %d, ret=%d", 
			achannels, ret);
		return ret;
	}
	if (output.empty()) {
		ret = ERROR_ENCODER_OUTPUT;
		srs_error("invalid empty output, ret=%d", ret);
		return ret;
	}
	
	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 = 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.
    // 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_INGESTER_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_info("connect to server success. server=%s, ip=%s, port=%d", server.c_str(), ip.c_str(), port);
    
    srs_trace("edge 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 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;
}
Esempio n. 5
0
 void SrsThread::thread_cycle()
 {
     int ret = ERROR_SUCCESS;
     
     _srs_context->generate_id();
     srs_info("thread %s cycle start", _name);
     
     _cid = _srs_context->get_id();
     
     srs_assert(handler);
     handler->on_thread_start();
     
     // thread is running now.
     really_terminated = false;
     
     // wait for cid to ready, for parent thread to get the cid.
     while (!can_run && loop) {
         st_usleep(10 * 1000);
     }
     
     while (loop) {
         if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
             srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret);
             goto failed;
         }
         srs_info("thread %s on before cycle success");
         
         if ((ret = handler->cycle()) != ERROR_SUCCESS) {
             if (!srs_is_client_gracefully_close(ret) && !srs_is_system_control_error(ret)) {
                 srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);
             }
             goto failed;
         }
         srs_info("thread %s cycle success", _name);
         
         if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
             srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret);
             goto failed;
         }
         srs_info("thread %s on end cycle success", _name);
         
     failed:
         if (!loop) {
             break;
         }
         
         // to improve performance, donot sleep when interval is zero.
         // @see: https://github.com/ossrs/srs/issues/237
         if (cycle_interval_us != 0) {
             st_usleep(cycle_interval_us);
         }
     }
     
     // readly terminated now.
     really_terminated = true;
     
     // when thread terminated normally, also disposed.
     // we must set to disposed before the on_thread_stop, which may free the thread.
     // @see https://github.com/ossrs/srs/issues/546
     disposed = true;
     
     handler->on_thread_stop();
     srs_info("thread %s cycle finished", _name);
 }
int SrsRtmp::start_fmle_publish(int stream_id)
{
	int ret = ERROR_SUCCESS;
	
	// FCPublish
	double fc_publish_tid = 0;
	if (true) {
		SrsCommonMessage* msg = NULL;
		SrsFMLEStartPacket* pkt = NULL;
		if ((ret = srs_rtmp_expect_message<SrsFMLEStartPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
			srs_error("recv FCPublish message failed. ret=%d", ret);
			return ret;
		}
		srs_info("recv FCPublish request message success.");
		
		SrsAutoFree(SrsCommonMessage, msg, false);
		fc_publish_tid = pkt->transaction_id;
	}
	// FCPublish response
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid);
		
		msg->set_packet(pkt, 0);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send FCPublish response message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send FCPublish response message success.");
	}
	
	// createStream
	double create_stream_tid = 0;
	if (true) {
		SrsCommonMessage* msg = NULL;
		SrsCreateStreamPacket* pkt = NULL;
		if ((ret = srs_rtmp_expect_message<SrsCreateStreamPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
			srs_error("recv createStream message failed. ret=%d", ret);
			return ret;
		}
		srs_info("recv createStream request message success.");
		
		SrsAutoFree(SrsCommonMessage, msg, false);
		create_stream_tid = pkt->transaction_id;
	}
	// createStream response
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id);
		
		msg->set_packet(pkt, 0);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send createStream response message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send createStream response message success.");
	}
	
	// publish
	if (true) {
		SrsCommonMessage* msg = NULL;
		SrsPublishPacket* pkt = NULL;
		if ((ret = srs_rtmp_expect_message<SrsPublishPacket>(protocol, &msg, &pkt)) != ERROR_SUCCESS) {
			srs_error("recv publish message failed. ret=%d", ret);
			return ret;
		}
		srs_info("recv publish request message success.");
		
		SrsAutoFree(SrsCommonMessage, msg, false);
	}
	// publish response onFCPublish(NetStream.Publish.Start)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
		
		pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH;
		pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
		pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send onFCPublish(NetStream.Publish.Start) message success.");
	}
	// publish response onStatus(NetStream.Publish.Start)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
		
		pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
		pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart));
		pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream."));
		pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send onStatus(NetStream.Publish.Start) message success.");
	}
	
	srs_info("FMLE publish success.");
	
	return ret;
}
int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause)
{
	int ret = ERROR_SUCCESS;
	
	if (is_pause) {
		// onStatus(NetStream.Pause.Notify)
		if (true) {
			SrsCommonMessage* msg = new SrsCommonMessage();
			SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
			
			pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
			pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause));
			pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream."));
			
			msg->set_packet(pkt, stream_id);
			
			if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
				srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret);
				return ret;
			}
			srs_info("send onStatus(NetStream.Pause.Notify) message success.");
		}
		// StreamEOF
		if (true) {
			SrsCommonMessage* msg = new SrsCommonMessage();
			SrsUserControlPacket* pkt = new SrsUserControlPacket();
			
			pkt->event_type = SrcPCUCStreamEOF;
			pkt->event_data = stream_id;
			msg->set_packet(pkt, 0);
			
			if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
				srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret);
				return ret;
			}
			srs_info("send PCUC(StreamEOF) message success.");
		}
	} else {
		// onStatus(NetStream.Unpause.Notify)
		if (true) {
			SrsCommonMessage* msg = new SrsCommonMessage();
			SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
			
			pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
			pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause));
			pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream."));
			
			msg->set_packet(pkt, stream_id);
			
			if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
				srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret);
				return ret;
			}
			srs_info("send onStatus(NetStream.Unpause.Notify) message success.");
		}
		// StreanBegin
		if (true) {
			SrsCommonMessage* msg = new SrsCommonMessage();
			SrsUserControlPacket* pkt = new SrsUserControlPacket();
			
			pkt->event_type = SrcPCUCStreamBegin;
			pkt->event_data = stream_id;
			msg->set_packet(pkt, 0);
			
			if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
				srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret);
				return ret;
			}
			srs_info("send PCUC(StreanBegin) message success.");
		}
	}
	
	return ret;
}
int SrsRtmp::start_play(int stream_id)
{
	int ret = ERROR_SUCCESS;
	
	// StreamBegin
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsUserControlPacket* pkt = new SrsUserControlPacket();
		
		pkt->event_type = SrcPCUCStreamBegin;
		pkt->event_data = stream_id;
		msg->set_packet(pkt, 0);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send PCUC(StreamBegin) message success.");
	}
	
	// onStatus(NetStream.Play.Reset)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
		
		pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
		pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset));
		pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream."));
		pkt->data->set(StatusDetails, new SrsAmf0String("stream"));
		pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send onStatus(NetStream.Play.Reset) message success.");
	}
	
	// onStatus(NetStream.Play.Start)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket();
		
		pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus));
		pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart));
		pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream."));
		pkt->data->set(StatusDetails, new SrsAmf0String("stream"));
		pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID));
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send onStatus(NetStream.Play.Reset) message success.");
	}
	
	// |RtmpSampleAccess(false, false)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket();
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send |RtmpSampleAccess(false, false) message success.");
	}
	
	// onStatus(NetStream.Data.Start)
	if (true) {
		SrsCommonMessage* msg = new SrsCommonMessage();
		SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket();
		
		pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart));
		
		msg->set_packet(pkt, stream_id);
		
		if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) {
			srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret);
			return ret;
		}
		srs_info("send onStatus(NetStream.Data.Start) message success.");
	}
	
	srs_info("start play success.");
	
	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;
}