Beispiel #1
0
 void SrsThread::dispose()
 {
     if (disposed) {
         return;
     }
     
     // the interrupt will cause the socket to read/write error,
     // which will terminate the cycle thread.
     st_thread_interrupt(tid);
     
     // when joinable, wait util quit.
     if (_joinable) {
         // wait the thread to exit.
         int ret = st_thread_join(tid, NULL);
         if (ret) {
             srs_warn("core: ignore join thread failed.");
         }
     }
     
     // wait the thread actually terminated.
     // sometimes the thread join return -1, for example,
     // when thread use st_recvfrom, the thread join return -1.
     // so here, we use a variable to ensure the thread stopped.
     // @remark even the thread not joinable, we must ensure the thread stopped when stop.
     while (!really_terminated) {
         st_usleep(10 * 1000);
         
         if (really_terminated) {
             break;
         }
         srs_warn("core: wait thread to actually terminated");
     }
     
     disposed = true;
 }
int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd)
{
    int ret = ERROR_SUCCESS;
    
    int max_connections = _srs_config->get_max_connections();
    if ((int)conns.size() >= max_connections) {
        int fd = st_netfd_fileno(client_stfd);
        
        srs_error("exceed the max connections, drop client: "
            "clients=%d, max=%d, fd=%d", (int)conns.size(), max_connections, fd);
            
        srs_close_stfd(client_stfd);
        
        return ret;
    }
    
    SrsConnection* conn = NULL;
    if (type == SrsListenerRtmpStream) {
        conn = new SrsRtmpConn(this, client_stfd);
    } else if (type == SrsListenerHttpApi) {
#ifdef SRS_AUTO_HTTP_API
        conn = new SrsHttpApi(this, client_stfd, http_api_handler);
#else
        srs_warn("close http client for server not support http-api");
        srs_close_stfd(client_stfd);
        return ret;
#endif
    } else if (type == SrsListenerHttpStream) {
#ifdef SRS_AUTO_HTTP_SERVER
        conn = new SrsHttpConn(this, client_stfd, http_stream_handler);
#else
        srs_warn("close http client for server not support http-server");
        srs_close_stfd(client_stfd);
        return ret;
#endif
    } else {
        // TODO: FIXME: handler others
    }
    srs_assert(conn);
    
    // directly enqueue, the cycle thread will remove the client.
    conns.push_back(conn);
    srs_verbose("add conn to vector.");
    
    // cycle will start process thread and when finished remove the client.
    // @remark never use the conn, for it maybe destroyed.
    if ((ret = conn->start()) != ERROR_SUCCESS) {
        return ret;
    }
    srs_verbose("conn started success.");

    srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret);
    
    return ret;
}
void check_macro_features()
{
    // for special features.
#ifdef SRS_AUTO_HTTP_SERVER
    srs_warn("http server is dev feature, @see %s", RTMP_SIG_SRS_HTTP_SERVER);
#endif

#if VERSION_MAJOR > 1
    #warning "using develop SRS, please use release instead."
    srs_warn("SRS %s is develop branch, please use %s instead", RTMP_SIG_SRS_VERSION, RTMP_SIG_SRS_RELEASE);
#endif
}
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_MEMINFO_RESOLUTION_TIMES);
    max = srs_max(max, SRS_SYS_PLATFORM_INFO_RESOLUTION_TIMES);
    
    // the deamon thread, update the time cache
    while (true) {
        for (int i = 1; i < max + 1; 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_update_system_time_ms();
            }
            if ((i % SRS_SYS_RUSAGE_RESOLUTION_TIMES) == 0) {
                srs_update_system_rusage();
            }
            if ((i % SRS_SYS_CPU_STAT_RESOLUTION_TIMES) == 0) {
                srs_update_proc_stat();
            }
            if ((i % SRS_SYS_MEMINFO_RESOLUTION_TIMES) == 0) {
                srs_update_meminfo();
            }
            if ((i % SRS_SYS_PLATFORM_INFO_RESOLUTION_TIMES) == 0) {
                srs_update_platform_info();
            }
        }
    }

    return ret;
}
int SrsConnection::cycle()
{
    int ret = ERROR_SUCCESS;
    
    _srs_context->generate_id();
    ip = srs_get_peer_ip(st_netfd_fileno(stfd));
    
    ret = do_cycle();
    
    // if socket io error, set to closed.
    if (srs_is_client_gracefully_close(ret)) {
        ret = ERROR_SOCKET_CLOSED;
    }
    
    // success.
    if (ret == ERROR_SUCCESS) {
        srs_trace("client finished.");
    }
    
    // client close peer.
    if (ret == ERROR_SOCKET_CLOSED) {
        srs_warn("client disconnect peer. ret=%d", ret);
    }
    
    // set loop to stop to quit.
    pthread->stop_loop();

    return ERROR_SUCCESS;
}
bool get_proc_system_stat(SrsProcSystemStat& r)
{
    FILE* f = fopen("/proc/stat", "r");
    if (f == NULL) {
        srs_warn("open system cpu stat failed, ignore");
        return false;
    }
    
    for (;;) {
        int ret = fscanf(f, "%4s %lu %lu %lu %lu %lu "
            "%lu %lu %lu %lu\n", 
            r.label, &r.user, &r.nice, &r.sys, &r.idle, &r.iowait,
            &r.irq, &r.softirq, &r.steal, &r.guest);
        r.ok = false;
        
        if (ret == EOF) {
            break;
        }
        
        if (strcmp("cpu", r.label) == 0) {
            r.ok = true;
            break;
        }
    }
    
    fclose(f);
    
    return r.ok;
}
bool get_proc_self_stat(SrsProcSelfStat& r)
{
    FILE* f = fopen("/proc/self/stat", "r");
    if (f == NULL) {
        srs_warn("open self cpu stat failed, ignore");
        return false;
    }
    
    int ret = fscanf(f, "%d %32s %c %d %d %d %d "
        "%d %u %lu %lu %lu %lu "
        "%lu %lu %ld %ld %ld %ld "
        "%ld %ld %llu %lu %ld "
        "%lu %lu %lu %lu %lu "
        "%lu %lu %lu %lu %lu "
        "%lu %lu %lu %d %d "
        "%u %u %llu "
        "%lu %ld", 
        &r.pid, r.comm, &r.state, &r.ppid, &r.pgrp, &r.session, &r.tty_nr,
        &r.tpgid, &r.flags, &r.minflt, &r.cminflt, &r.majflt, &r.cmajflt,
        &r.utime, &r.stime, &r.cutime, &r.cstime, &r.priority, &r.nice,
        &r.num_threads, &r.itrealvalue, &r.starttime, &r.vsize, &r.rss,
        &r.rsslim, &r.startcode, &r.endcode, &r.startstack, &r.kstkesp,
        &r.kstkeip, &r.signal, &r.blocked, &r.sigignore, &r.sigcatch,
        &r.wchan, &r.nswap, &r.cnswap, &r.exit_signal, &r.processor,
        &r.rt_priority, &r.policy, &r.delayacct_blkio_ticks, 
        &r.guest_time, &r.cguest_time);
    
    fclose(f);
        
    if (ret >= 0) {
        r.ok = true;
    }
    
    return r.ok;
}
 int SrsDH::copy_shared_key(const char* ppkey, int32_t ppkey_size, char* skey, int32_t& skey_size)
 {
     int ret = ERROR_SUCCESS;
     
     BIGNUM* ppk = NULL;
     if ((ppk = BN_bin2bn((const unsigned char*)ppkey, ppkey_size, 0)) == NULL) {
         ret = ERROR_OpenSslGetPeerPublicKey;
         return ret;
     }
     
     // if failed, donot return, do cleanup, @see ./test/dhtest.c:168
     // maybe the key_size is 127, but dh will write all 128bytes skey,
     // so, donot need to set/initialize the skey.
     // @see https://github.com/winlinvip/simple-rtmp-server/issues/165
     int32_t key_size = DH_compute_key((unsigned char*)skey, ppk, pdh);
     
     if (key_size < ppkey_size) {
         srs_warn("shared key size=%d, ppk_size=%d", key_size, ppkey_size);
     }
     
     if (key_size < 0 || key_size > skey_size) {
         ret = ERROR_OpenSslComputeSharedKey;
     } else {
         skey_size = key_size;
     }
     
     if (ppk) {
         BN_free(ppk);
     }
     
     return ret;
 }
void SrsEncoder::encoder_cycle()
{
	int ret = ERROR_SUCCESS;
	
	log_context->generate_id();
	srs_trace("encoder cycle start");
	
	while (loop) {
		if ((ret = cycle()) != ERROR_SUCCESS) {
			srs_warn("encoder cycle failed, ignored and retry, ret=%d", ret);
		} else {
			srs_info("encoder cycle success, retry");
		}
		
		if (!loop) {
			break;
		}
		
		st_usleep(SRS_ENCODER_SLEEP_MS * 1000);
	}
	
	// kill ffmpeg when finished and it alive
	std::vector<SrsFFMPEG*>::iterator it;
	for (it = ffmpegs.begin(); it != ffmpegs.end(); ++it) {
		SrsFFMPEG* ffmpeg = *it;
		ffmpeg->stop();
	}
	
	srs_trace("encoder cycle finished");
}
void SrsThread::thread_cycle()
{
    int ret = ERROR_SUCCESS;
    
    _srs_context->generate_id();
    srs_info("thread cycle start");
    
    _cid = _srs_context->get_id();
    
    srs_assert(handler);
    handler->on_thread_start();
    
    // 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 on before cycle failed, ignored and retry, ret=%d", ret);
            goto failed;
        }
        srs_info("thread on before cycle success");
        
        if ((ret = handler->cycle()) != ERROR_SUCCESS) {
            srs_warn("thread cycle failed, ignored and retry, ret=%d", ret);
            goto failed;
        }
        srs_info("thread cycle success");
        
        if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
            srs_warn("thread on end cycle failed, ignored and retry, ret=%d", ret);
            goto failed;
        }
        srs_info("thread on end cycle success");

failed:
        if (!loop) {
            break;
        }
        
        st_usleep(cycle_interval_us);
    }
    
    handler->on_thread_stop();
    srs_info("thread cycle finished");
}
int SrsServer::cycle()
{
    int ret = ERROR_SUCCESS;

    ret = do_cycle();

#ifdef SRS_AUTO_GPERF_MC
    destroy();
    
    srs_warn("sleep a long time for system st-threads to cleanup.");
    st_usleep(3 * 1000 * 1000);
    srs_warn("system quit");
#else
    srs_warn("main cycle terminated, system quit normally.");
    exit(0);
#endif
    
    return ret;
}
void srs_update_system_time_ms()
{
    timeval now;
    
    if (gettimeofday(&now, NULL) < 0) {
        srs_warn("gettimeofday failed, ignore");
        return;
    }

    // @see: https://github.com/winlinvip/simple-rtmp-server/issues/35
    // we must convert the tv_sec/tv_usec to int64_t.
    int64_t now_us = ((int64_t)now.tv_sec) * 1000 * 1000 + (int64_t)now.tv_usec;
    if (now_us < _srs_system_time_us_cache) {
        srs_warn("system time negative, "
            "history=%"PRId64"us, now=%"PRId64"", _srs_system_time_us_cache, now_us);
    }
    
    _srs_system_time_us_cache = now_us;
}
void SrsThread::thread_cycle()
{
	int ret = ERROR_SUCCESS;
	
	srs_assert(handler);
	
	log_context->generate_id();
	srs_trace("thread cycle start");

	handler->on_end_cycle();
	
	loop = true;
	while (loop) {
		if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
			srs_warn("thread on before cycle failed, ignored and retry, ret=%d", ret);
			goto failed;
		}
		srs_info("thread on before cycle success");
		
		if ((ret = handler->cycle()) != ERROR_SUCCESS) {
			srs_warn("thread cycle failed, ignored and retry, ret=%d", ret);
			goto failed;
		}
		srs_info("thread cycle success");
		
		if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
			srs_warn("thread on end cycle failed, ignored and retry, ret=%d", ret);
			goto failed;
		}
		srs_info("thread on end cycle success");

failed:
		if (!loop) {
			break;
		}
		
		st_usleep(cycle_interval_us);
	}
	
	handler->on_leave_loop();
	srs_trace("thread cycle finished");
}
int SrsListener::cycle()
{
    int ret = ERROR_SUCCESS;
    
    st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
    
    if(client_stfd == NULL){
        // ignore error.
        srs_warn("ignore accept thread stoppped for accept client error");
        return ret;
    }
    srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
    
    if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) {
        srs_warn("accept client error. 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);
	
	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://";
	tc_url += vhost;
	tc_url += "/";
	tc_url += req->app;
	
	// dead loop check
	std::string source_ep = req->vhost;
	source_ep += ":";
	source_ep += req->port;
	
	std::string dest_ep = vhost;
	dest_ep += ":";
	dest_ep += s_port;
	
	if (source_ep == dest_ep) {
		ret = ERROR_SYSTEM_FORWARD_LOOP;
		srs_warn("farder 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, stream: %s/%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;
	}
	
	return ret;
}
void srs_update_system_rusage()
{
    if (getrusage(RUSAGE_SELF, &_srs_system_rusage.r) < 0) {
        srs_warn("getrusage failed, ignore");
        return;
    }
        
    srs_update_system_time_ms();
    _srs_system_rusage.sample_time = srs_get_system_time_ms();
    
    _srs_system_rusage.ok = true;
}
void SrsServer::destroy()
{
    srs_warn("start destroy server");
    
    _srs_config->unsubscribe(this);
    
    close_listeners(SrsListenerRtmpStream);
    close_listeners(SrsListenerHttpApi);
    close_listeners(SrsListenerHttpStream);

#ifdef SRS_AUTO_INGEST
    ingester->stop();
#endif
    
#ifdef SRS_AUTO_HTTP_API
    srs_freep(http_api_handler);
#endif

#ifdef SRS_AUTO_HTTP_SERVER
    srs_freep(http_stream_handler);
#endif

#ifdef SRS_AUTO_HTTP_PARSER
    srs_freep(http_heartbeat);
#endif

#ifdef SRS_AUTO_INGEST
    srs_freep(ingester);
#endif
    
    if (pid_fd > 0) {
        ::close(pid_fd);
        pid_fd = -1;
    }
    
    srs_freep(signal_manager);
    srs_freep(kbps);
    
    for (std::vector<SrsConnection*>::iterator it = conns.begin(); it != conns.end();) {
        SrsConnection* conn = *it;

        // remove the connection, then free it,
        // for the free will remove itself from server,
        // when erased here, the remove of server will ignore.
        it = conns.erase(it);

        srs_freep(conn);
    }
    conns.clear();

    SrsSource::destroy();
}
void srs_update_meminfo()
{
    FILE* f = fopen("/proc/meminfo", "r");
    if (f == NULL) {
        srs_warn("open meminfo failed, ignore");
        return;
    }
    
    SrsMemInfo& r = _srs_system_meminfo;
    r.ok = false;
    
    for (;;) {
        static char label[64];
        static unsigned long value;
        static char postfix[64];
        int ret = fscanf(f, "%64s %lu %64s\n", label, &value, postfix);
        
        if (ret == EOF) {
            break;
        }
        
        if (strcmp("MemTotal:", label) == 0) {
            r.MemTotal = value;
        } else if (strcmp("MemFree:", label) == 0) {
            r.MemFree = value;
        } else if (strcmp("Buffers:", label) == 0) {
            r.Buffers = value;
        } else if (strcmp("Cached:", label) == 0) {
            r.Cached = value;
        } else if (strcmp("SwapTotal:", label) == 0) {
            r.SwapTotal = value;
        } else if (strcmp("SwapFree:", label) == 0) {
            r.SwapFree = value;
        }
    }
    
    fclose(f);
    
    r.sample_time = srs_get_system_time_ms();
    r.MemActive = r.MemTotal - r.MemFree;
    r.RealInUse = r.MemActive - r.Buffers - r.Cached;
    r.NotInUse = r.MemTotal - r.RealInUse;
    
    r.ok = true;
    if (r.MemTotal > 0) {
        r.percent_ram = (float)(r.RealInUse / (double)r.MemTotal);
    }
    if (r.SwapTotal > 0) {
        r.percent_swap = (float)((r.SwapTotal - r.SwapFree) / (double)r.SwapTotal);
    }
}
void srs_update_platform_info()
{
    SrsPlatformInfo& r = _srs_system_platform_info;
    r.ok = true;
    
    if (true) {
        FILE* f = fopen("/proc/uptime", "r");
        if (f == NULL) {
            srs_warn("open uptime failed, ignore");
            return;
        }
        
        int ret = fscanf(f, "%lf %lf\n", &r.os_uptime, &r.os_ilde_time);
    
        fclose(f);

        if (ret < 0) {
            r.ok = false;
        }
    }
    
    if (true) {
        FILE* f = fopen("/proc/loadavg", "r");
        if (f == NULL) {
            srs_warn("open loadavg failed, ignore");
            return;
        }
        
        int ret = fscanf(f, "%lf %lf %lf\n", 
            &r.load_one_minutes, &r.load_five_minutes, &r.load_fifteen_minutes);
    
        fclose(f);

        if (ret < 0) {
            r.ok = false;
        }
    }
}
int SrsEdgeIngester::cycle()
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = connect_server()) != ERROR_SUCCESS) {
        return ret;
    }
    srs_assert(client);

    client->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
    client->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);

    SrsRequest* req = _req;
    
    if ((ret = client->handshake()) != ERROR_SUCCESS) {
        srs_error("handshake with server failed. ret=%d", ret);
        return ret;
    }
    if ((ret = client->connect_app(req->app, req->tcUrl, req)) != ERROR_SUCCESS) {
        srs_error("connect with server failed, tcUrl=%s. ret=%d", req->tcUrl.c_str(), ret);
        return ret;
    }
    if ((ret = client->create_stream(stream_id)) != ERROR_SUCCESS) {
        srs_error("connect with server failed, stream_id=%d. ret=%d", stream_id, ret);
        return ret;
    }
    
    if ((ret = client->play(req->stream, stream_id)) != ERROR_SUCCESS) {
        srs_error("connect with server failed, stream=%s, stream_id=%d. ret=%d", 
            req->stream.c_str(), stream_id, ret);
        return ret;
    }
    
    if ((ret = _source->on_publish()) != ERROR_SUCCESS) {
        srs_error("edge pull stream then publish to edge failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = _edge->on_ingest_play()) != ERROR_SUCCESS) {
        return ret;
    }
    
    ret = ingest();
    if (srs_is_client_gracefully_close(ret)) {
        srs_warn("origin disconnected, retry. ret=%d", ret);
        ret = ERROR_SUCCESS;
    }
    
    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 = 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_FORWARDER_TIMEOUT_US;
    if ((ret = srs_socket_connect(server, port, timeout, &stfd)) != ERROR_SUCCESS) {
        srs_warn("edge push 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);
    
    // open socket.
    srs_trace("edge push connected, stream=%s, tcUrl=%s to server=%s, port=%d",
        _req->stream.c_str(), _req->tcUrl.c_str(), server.c_str(), port);
    
    return ret;
}
void SrsServer::remove(SrsConnection* conn)
{
    std::vector<SrsConnection*>::iterator it = std::find(conns.begin(), conns.end(), conn);
    
    // removed by destroy, ignore.
    if (it == conns.end()) {
        srs_warn("server moved connection, ignore.");
        return;
    }
    
    conns.erase(it);
    
    srs_info("conn removed. conns=%d", (int)conns.size());
    
    // all connections are created by server,
    // so we free it here.
    srs_freep(conn);
}
void SrsThread::stop()
{
    if (tid) {
        loop = false;
        
        // the interrupt will cause the socket to read/write error,
        // which will terminate the cycle thread.
        st_thread_interrupt(tid);
        
        // wait the thread to exit.
        int ret = st_thread_join(tid, NULL);
        // TODO: FIXME: the join maybe failed, should use a variable to ensure thread terminated.
        if (ret != 0) {
            srs_warn("join thread failed. code=%d", ret);
        }
        
        tid = NULL;
    }
}
 int SrsDH::initialize(bool ensure_128bytes_public_key)
 {
     int ret = ERROR_SUCCESS;
     
     for (;;) {
         if ((ret = do_initialize()) != ERROR_SUCCESS) {
             return ret;
         }
         
         if (ensure_128bytes_public_key) {
             int32_t key_size = BN_num_bytes(pdh->pub_key);
             if (key_size != 128) {
                 srs_warn("regenerate 128B key, current=%dB", key_size);
                 continue;
             }
         }
         
         break;
     }
     
     return ret;
 }
Beispiel #25
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 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 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;
}
/**
* 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 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;
}