SRS(simple-rtmp-server)流媒体服务器源码分析--启动

Simple Rtmp Server的安装与简单使用 Simple Rtmp Server是一个国人编写的开源的RTMP/HLS流媒体服务器. 功能与nginx-rtmp-module类似, 可以实现rtmp/hls的分发.   有关nginx-rtmp-module的可参照: http://blog.youkuaiyun.com/redstarofsleep/article/details/45092147   编译与安装过程十分的简单     ./ 阅读详情

SRS(simple-rtmp-server)流媒体服务器源码分析--系统启动

一、前言

       小卒最近看SRS源码,随手写下博客,其一为了整理思路,其二也是为日后翻看方便。如果不足之处,请指教!

首先总结一下SRS源码的优点:

       1、轻量级,代码结构清楚,目前SRS3.0代码8万行左右,但几乎满足直播业务的所有要求。

       2、SRS采用State Threads,支持高并发量,高性能。

       3、SRS支持rtmp和hls,满足PC和移动直播要求。

       4、SRS支持集群部署。小集群Forward,大集群edge。

代码分析可分为两个阶段:
       一:分析代码框架,理清楚组织流程
       二:分析代码细节,熟悉SRS工作原理

二、代码分析

       相关SRS源码其他总结:

           SRS(simple-rtmp-server)流媒体服务器源码分析--系统启动
           SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play

           SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP信息Publish

           SRS(simple-rtmp-server)流媒体服务器源码分析--HLS切片

现阶段,我主要以代码框架梳理为主。Srs源码框架如下图:

       
         系统在启动时,初始化相关类,监听相关端口,若来一个访问请求,则为该链接创建一个线程,专门处理与该链接的操作。
         main函数在srs_main_server.cpp这个文件中。在main函数中,启动参数在这里不做过多介绍。直接从run()-> run_master()看起。
int run_master()
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = _srs_server->initialize_st()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = _srs_server->initialize_signal()) != ERROR_SUCCESS) {
        return ret;
    }
    //将pid进程号写进文件
    if ((ret = _srs_server->acquire_pid_file()) != ERROR_SUCCESS) {
        return ret;
    }
    //客户端监听
    if ((ret = _srs_server->listen()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = _srs_server->register_signal()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = _srs_server->http_handle()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = _srs_server->ingest()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = _srs_server->cycle()) != ERROR_SUCCESS) {
        return ret;
    }
    
    return 0;
}

进入客户监听

 if ((ret = _srs_server->listen()) != ERROR_SUCCESS) {
        return ret;
    }
 监听内容:  不同的连接请求,有不同的监听。
int SrsServer::listen()
{
    int ret = ERROR_SUCCESS;
    // 创建一个rtmp的Streamlistener
    if ((ret = listen_rtmp()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = listen_http_api()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = listen_http_stream()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = listen_stream_caster()) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

1、首先分析RTMP连接 

int SrsServer::listen_rtmp()
{
    int ret = ERROR_SUCCESS;
    
    // stream service port.
    std::vector<std::string> ip_ports = _srs_config->get_listens();
    srs_assert((int)ip_ports.size() > 0);
    
    close_listeners(SrsListenerRtmpStream);
    
    for (int i = 0; i < (int)ip_ports.size(); i++) {
        SrsListener* listener = new SrsStreamListener(this, SrsListenerRtmpStream);
        listeners.push_back(listener);
        
        std::string ip;
        int port;
        srs_parse_endpoint(ip_ports[i], ip, port);
        
        if ((ret = listener->listen(ip, port)) != ERROR_SUCCESS) {
            srs_error("RTMP stream listen at %s:%d failed. ret=%d", ip.c_str(), port, ret);
            return ret;
        }
    }
    
    return ret;
}
        这里是listen_rtmp()函数,你也可以去看看listen_http_api()函数、listen_http_stream()函数,其实结构都很相似,只是在创建SrsStreamListener对象时,传入了不同的参数SrsListenerRtmpStream、SrsListenerHttpApi、SrsListenerHttpStream,代表了不同类型的监听对象。
// listen_rtmp 中listen监听走这里了。
int SrsStreamListener::listen(string i, int p)
{
    int ret = ERROR_SUCCESS;
    
    ip = i;
    port = p;

    srs_freep(listener);
    listener = new SrsTcpListener(this, ip, port);

    if ((ret = listener->listen()) != ERROR_SUCCESS) {
        srs_error("tcp listen failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("listen thread current_cid=%d, "
        "listen at port=%d, type=%d, fd=%d started success, ep=%s:%d",
        _srs_context->get_id(), p, type, listener->fd(), i.c_str(), p);

    srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd());

    return ret;
}
注意,这里有大量纯虚函数,不要走错路了。进入TCP监听代码
// rtmp tcp监听
int SrsTcpListener::listen()
{
//C++ Socket编程
    int ret = ERROR_SUCCESS;
    // 1、创建套接字,流式Socket(SOCK_STREAM)
    if ((_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
        ret = ERROR_SOCKET_CREATE;
        srs_error("create linux socket error. port=%d, ret=%d", port, ret);
        return ret;
    }
    srs_verbose("create linux socket success. port=%d, fd=%d", port, _fd);
    
    int reuse_socket = 1;
    if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
        ret = ERROR_SOCKET_SETREUSE;
        srs_error("setsockopt reuse-addr error. port=%d, ret=%d", port, ret);
        return ret;
    }
    srs_verbose("setsockopt reuse-addr success. port=%d, fd=%d", port, _fd);
    
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
	// 2、绑定套接字到一个IP地址和一个端口上
    if (bind(_fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
        ret = ERROR_SOCKET_BIND;
        srs_error("bind socket error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("bind socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
    // 3、将套接字设置为监听模式等待连接请求
    if (::listen(_fd, SERVER_LISTEN_BACKLOG) == -1) {
        ret = ERROR_SOCKET_LISTEN;
        srs_error("listen socket error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("listen socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
    
    if ((_stfd = st_netfd_open_socket(_fd)) == NULL){
        ret = ERROR_ST_OPEN_SOCKET;
        srs_error("st_netfd_open_socket open socket failed. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("st open socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
    // 4、等到连接一个客户之后,开启一个新的线程
    if ((ret = pthread->start()) != ERROR_SUCCESS) {
        srs_error("st_thread_create listen thread error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("create st listen thread success, ep=%s:%d", ip.c_str(), port);
    
    return ret;
}
       此代码为C++ TCP  Socket代码,思路比较清晰,可以看到,每接受到一个rtmp访问请求,创建一个”线程“,这里暂时将其称为线程,后面再做具体介绍。创建线程代码如下:
int SrsReusableThread::start()
{
    return pthread->start();
}

int SrsThread::start()
{
    int ret = ERROR_SUCCESS;

    if(tid) {
        srs_info("thread %s already running.", _name);
        return ret;
    }
    if((tid = st_thread_create(thread_fun, this, (_joinable? 1:0), 0)) == NULL){
        ret = ERROR_ST_CREATE_CYCLE_THREAD;
	srs_error("st_thread_create failed. ret=%d", ret);
	return ret;
    }
    disposed = false;
    // we set to loop to true for thread to run.
    loop = true;
    // wait for cid to ready, for parent thread to get the cid.
    while (_cid < 0) {
        st_usleep(10 * 1000);
    }
    // now, cycle thread can run.
    can_run = true;
    return ret;
}

       来到了st_thread_create,这里要注意,这是SRS开源项目具有高并发,高性能的重要一步。这里创建的是协程,不是线程。协程是有别于进程和线程的一种组件,具有进程的独立性和线程的轻量级,听说微信能够支持8亿用户量,也是采用协程这种网络服务框架:http://www.infoq.com/cn/articles/CplusStyleCorourtine-At-Wechat。

从这里可以看出,srs是一个单线程的服务器,采用协程,主持高并发,高性能。

创建协程,协程函数为:thread_fun()

// 每连链接一个用户,创建一个协程程,该函数为协程函数
void* SrsThread::thread_fun(void* arg)
{
	SrsThread* obj = (SrsThread*)arg;
	srs_assert(obj);
	// 进入线程循环
	obj->thread_cycle();
	
	// for valgrind to detect.
	SrsThreadContext* ctx = dynamic_cast<SrsThreadContext*>(_srs_context);
	if (ctx) {
		ctx->clear_cid();
	}
	
	st_thread_exit(NULL);
	
	return NULL;
}
此时,真正进入了协程循环处理
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", _name);
            // 注意纯虚函数的应用
            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;
        
        handler->on_thread_stop();
        srs_info("thread %s cycle finished", _name);
    }
       至此,一定要熟悉C++纯虚函数的引用,本人刚学了几天C++,对虚函数和纯虚函数在SRS源码中的应用很不习惯!  好了,进入循环ret = handler->cycle()
int SrsConnection::cycle()
{
    int ret = ERROR_SUCCESS;
    
    _srs_context->generate_id();
    id = _srs_context->get_id();
    
    ip = srs_get_peer_ip(st_netfd_fileno(stfd));
    //srs_trace("ip:%s", ip);
    
    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);
    }

    return ERROR_SUCCESS;
}
进入ret=do_cycle();
// TODO: return detail message when error for client.
int SrsRtmpConn::do_cycle()
{
    int ret = ERROR_SUCCESS;
    
    srs_trace("RTMP client ip=%s", ip.c_str());

    rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
    rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);
    
    if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
        srs_error("rtmp handshake failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("rtmp handshake success");
    
    if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) {
        srs_error("rtmp connect vhost/app failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("rtmp connect app success");
    
    // set client ip to request.
    req->ip = ip;
    
    // discovery vhost, resolve the vhost from config
    SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
    if (parsed_vhost) {
        req->vhost = parsed_vhost->arg0();
    }
    
    srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s",
        req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str());
    
    if (req->schema.empty() || req->vhost.empty() || req->port.empty() || req->app.empty()) {
        ret = ERROR_RTMP_REQ_TCURL;
        srs_error("discovery tcUrl failed. "
            "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d",
            req->tcUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str(), ret);
        return ret;
    }
    
    // check vhost
    if ((ret = check_vhost()) != ERROR_SUCCESS) {
        srs_error("check vhost failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("check vhost success.");
    
    srs_trace("connect app, "
        "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, args=%s", 
        req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), 
        req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),
        req->app.c_str(), (req->args? "(obj)":"null"));
    
    // show client identity
    if(req->args) {
        std::string srs_version;
        std::string srs_server_ip;
        int srs_pid = 0;
        int srs_id = 0;
        
        SrsAmf0Any* prop = NULL;
        if ((prop = req->args->ensure_property_string("srs_version")) != NULL) {
            srs_version = prop->to_str();
        }
        if ((prop = req->args->ensure_property_string("srs_server_ip")) != NULL) {
            srs_server_ip = prop->to_str();
        }
        if ((prop = req->args->ensure_property_number("srs_pid")) != NULL) {
            srs_pid = (int)prop->to_number();
        }
        if ((prop = req->args->ensure_property_number("srs_id")) != NULL) {
            srs_id = (int)prop->to_number();
        }
        
        srs_info("edge-srs ip=%s, version=%s, pid=%d, id=%d", 
            srs_server_ip.c_str(), srs_version.c_str(), srs_pid, srs_id);
        if (srs_pid > 0) {
            srs_trace("edge-srs ip=%s, version=%s, pid=%d, id=%d", 
                srs_server_ip.c_str(), srs_version.c_str(), srs_pid, srs_id);
        }
    }
    
    ret = service_cycle();
    
    http_hooks_on_close();

    return ret;
}
行了,rtmp连接就到这里,要不然都快到rtmp流接受代码了,和系统启动越走越远了,rtmp流接受后面再分析。

2、再分析http-api连接,回到int SrsServer::listen()函数中,梳理http-api链接

int SrsServer::listen_http_api()
{
    int ret = ERROR_SUCCESS;
    
#ifdef SRS_AUTO_HTTP_API
    close_listeners(SrsListenerHttpApi);
    if (_srs_config->get_http_api_enabled()) {
        SrsListener* listener = new SrsStreamListener(this, SrsListenerHttpApi);
        listeners.push_back(listener);
        
        std::string ep = _srs_config->get_http_api_listen();
        
        std::string ip;
        int port;
        srs_parse_endpoint(ep, ip, port);
        
        if ((ret = listener->listen(ip, port)) != ERROR_SUCCESS) {
            srs_error("HTTP api listen at %s:%d failed. ret=%d", ip.c_str(), port, ret);
            return ret;
        }
    }
#endif
    
    return ret;
}
listen_http_api()函数和listen_rtmp()函数内容非常像,再走到listener->listen()里面看看,结果来到了
int SrsStreamListener::listen(string i, int p)
{
    int ret = ERROR_SUCCESS;
    
    ip = i;
    port = p;

    srs_freep(listener);
    listener = new SrsTcpListener(this, ip, port);

    if ((ret = listener->listen()) != ERROR_SUCCESS) {
        srs_error("tcp listen failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("listen thread current_cid=%d, "
        "listen at port=%d, type=%d, fd=%d started success, ep=%s:%d",
        _srs_context->get_id(), p, type, listener->fd(), i.c_str(), p);

    srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd());

    return ret;
}
和rtmp链接监听机制完全一样,只是type不同而已
enum SrsListenerType 
{
    // RTMP client,
    SrsListenerRtmpStream       = 0,
    // HTTP api,
    SrsListenerHttpApi          = 1,
    // HTTP stream, HDS/HLS/DASH
    SrsListenerHttpStream       = 2,
    // UDP stream, MPEG-TS over udp.
    SrsListenerMpegTsOverUdp    = 3,
    // TCP stream, RTSP stream.
    SrsListenerRtsp             = 4,
    // TCP stream, FLV stream over HTTP.
    SrsListenerFlv              = 5,
};
我就看了两个链接监听,监听到此为止。
 

3、http api回调注册

回到run_master()函数中,从_srs_server->http_handle()看起。

int SrsServer::http_handle()
{
    int ret = ERROR_SUCCESS;
    
#ifdef SRS_AUTO_HTTP_API
    srs_assert(http_api_mux);
    if ((ret = http_api_mux->handle("/", new SrsHttpNotFoundHandler())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/", new SrsGoApiApi())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/", new SrsGoApiV1())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/versions", new SrsGoApiVersion())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/summaries", new SrsGoApiSummaries())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/rusages", new SrsGoApiRusages())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/authors", new SrsGoApiAuthors())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/features", new SrsGoApiFeatures())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != ERROR_SUCCESS) {
        return ret;
    }
    
    // test the request info.
    if ((ret = http_api_mux->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != ERROR_SUCCESS) {
        return ret;
    }
    // test the error code response.
    if ((ret = http_api_mux->handle("/api/v1/tests/errors", new SrsGoApiError())) != ERROR_SUCCESS) {
        return ret;
    }
    // test the redirect mechenism.
    if ((ret = http_api_mux->handle("/api/v1/tests/redirects", new SrsHttpRedirectHandler("/api/v1/tests/errors", SRS_CONSTS_HTTP_MovedPermanently))) != ERROR_SUCCESS) {
        return ret;
    }
    // test the http vhost.
    if ((ret = http_api_mux->handle("error.srs.com/api/v1/tests/errors", new SrsGoApiError())) != ERROR_SUCCESS) {
        return ret;
    }
    
    // TODO: FIXME: for console.
    // TODO: FIXME: support reload.
    std::string dir = _srs_config->get_http_stream_dir() + "/console";
    if ((ret = http_api_mux->handle("/console/", new SrsHttpFileServer(dir))) != ERROR_SUCCESS) {
        srs_error("http: mount console dir=%s failed. ret=%d", dir.c_str(), ret);
        return ret;
    }
    srs_trace("http: api mount /console to %s", dir.c_str());
#endif

    return ret;
}

该函数注册了http-api回调接口。可以参考:https://github.com/ossrs/srs/wiki/v2_CN_HTTPApi

比如我们可以访问http://ip:1985/api/v1  其中ip为SRS服务器地址,就可以看到从该接口返回srs服务器参数。

4、ingest(拉流,SRS主动去拉流,和推流相反)处理

注意:SRS对拉流的处理比较特殊, SRS拉流是通过ffmpeg工具去实现的,SRS代码只是实现简单的系统调用,这部分内容在后面的章节中详细说明。
int SrsIngester::start()
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = parse()) != ERROR_SUCCESS) {
        clear_engines();
        ret = ERROR_SUCCESS;
        return ret;
    }
    
    // even no ingesters, we must also start it,
    // for the reload may add more ingesters.
    
    // start thread to run all encoding engines.
    if ((ret = pthread->start()) != ERROR_SUCCESS) {
        srs_error("st_thread_create failed. ret=%d", ret);
        return ret;
    }
    srs_trace("ingest thread cid=%d, current_cid=%d", pthread->cid(), _srs_context->get_id());
    
    return ret;
}
到此,可以看出,和监听过程一样,进入int SrsThread::start()函数,只是传入对象不一样而已。
 

5、SRS自服务

int SrsServer::cycle()
{
    int ret = ERROR_SUCCESS;
	srs_trace("SrsServer")

    ret = do_cycle();

#ifdef SRS_AUTO_GPERF_MC
    destroy();
    
    // remark, for gmc, never invoke the exit().
    srs_warn("sleep a long time for system st-threads to cleanup.");
    st_usleep(3 * 1000 * 1000);
    srs_warn("system quit");
#else
    // normally quit with neccessary cleanup by dispose().
    srs_warn("main cycle terminated, system quit normally.");
    dispose();
    srs_trace("srs terminated");
    
    // for valgrind to detect.
    srs_freep(_srs_config);
    srs_freep(_srs_log);
    
    exit(0);
#endif
    
    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);
    
#ifdef SRS_AUTO_STAT
    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);
#endif
    
    // for asprocess.
    bool asprocess = _srs_config->get_asprocess();
    
    // the deamon thread, update the time cache
    while (true) {
        if(handler && (ret = handler->on_cycle((int)conns.size())) != ERROR_SUCCESS){
            srs_error("cycle handle failed. ret=%d", ret);
            return ret;
        }
            
        // the interval in config.
        int heartbeat_max_resolution = (int)(_srs_config->get_heartbeat_interval() / SRS_SYS_CYCLE_INTERVAL);
        
        // dynamic fetch the max.
        int temp_max = max;
        temp_max = srs_max(temp_max, heartbeat_max_resolution);
        
        for (int i = 0; i < temp_max; i++) {
            st_usleep(SRS_SYS_CYCLE_INTERVAL * 1000);
            
            // asprocess check.
            if (asprocess && ::getppid() != ppid) {
                srs_warn("asprocess ppid changed from %d to %d", ppid, ::getppid());
                return ret;
            }
            
            // gracefully quit for SIGINT or SIGTERM.
            if (signal_gracefully_quit) {
                srs_trace("cleanup for gracefully terminate.");
                return ret;
            }
        
            // 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
        
            // do reload the config.
            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.");
            }
            
            // notice the stream sources to cycle.
            if ((ret = SrsSource::cycle_all()) != ERROR_SUCCESS) {
                return ret;
            }
            
            // update the cache time
            if ((i % SRS_SYS_TIME_RESOLUTION_MS_TIMES) == 0) {
                srs_info("update current time cache.");
                srs_update_system_time_ms();
            }
            
#ifdef SRS_AUTO_STAT
            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 server kbps info.");
                resample_kbps();
            }
    #ifdef SRS_AUTO_HTTP_CORE
            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
#endif
            
            srs_info("server main thread loop");
        }
    }

    return ret;
}
主线程,更新srs时间和缓存!!至此,系统启动代码结构梳理完了。

三、总结

  • 启动不同的业务。
  • 监听不同的客户端类型。
  • 每链接一个客户端,SRS为其创建一个协程,专门负责该路链接信息交互。
  • SRS系统采用了协程网络服务框架,使得系统具有高并发,高性能等有点。
SRS(Simple RTMP Server)安装(搭建rtmp流媒体服务器) SRS 安装,控制服务命令 下载源码: git clone https://github.com/ossrs/srs.git 编译: cd srs/trunk ./configure && make 开启服务: 注意这里的srs.conf可以根据自己需要更改配置文件 ./objs/srs -c conf/srs.conf 启动服务 ps -ef | grep srs 停止... 阅读详情

相关推荐

SRS流媒体服务器——WebRTC推拉流演示

SRS流媒体服务器——WebRTC推拉流 目录 WebRTC推拉流配置 WebRTC拉流演示 WebRTC推流演示 SRS官方WebRTC文档:https://github.com/ossrs/srs/wiki/v4_CN_WebRTC#rtmp-to-rtc 1. WebRTC推拉流配置 webrtc是默认⽀持的(–rtc=on),使⽤默认的rtc配置(conf/rtc2rtmp.conf)就可以运行。 默认rtc2rtmp.conf配置如下: listen 19

努力努力再努力L 1万+

SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play

SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP消息play 前言:本章内容梳理SRS接受到rtmp信息之后,如何转发出去。同样这里先做代码梳理,本人也在源码熟悉之中,可能很多网友朋友最关心接受到rtmp之后,信息如果处理,如何缓存甚至如何转发给直播用户,这些内容目前还没有梳理完毕。 1、进入play流程     由于之前的博客中已经梳理了系统启

ManagerUser的博客 7473

FPGA—ZYNQ_BD Block Design学习(一)

1.第一步:打开Vivado软件,创建或打开一个工程。2.第二步:在Block Design中添加arm核心,并将其配置为IP核。3.第三步:配置arm核心的外设信息,如DDR接口、时钟频率、UART接口等。4.第四步:完成Block Design中的模块互联,生成PL端的比特流。 5.第五步:导出PS端的硬件信息,包括arm IP核的外设、速度、引脚和地址等信息。1创建bd 需要有ddr以及uart和时钟配置外设io口,这里就是zynq的管脚规定好的,根据原理图用哪个个勾选哪一个。 对外设uart的配

m0_58129662的博客 1028

SRS Simple-RTMP-Server 全面介绍、教程与指南

SRS(Simple Real-Time Media Server)是一个开源的流媒体服务器,旨在提供高效、稳定的实时音视频服务。SRS专注于实现互联网级别的集群部署,追求概念完整性和代码实现的简洁性。其主要功能包括支持多种流媒体协议、实现直播流的推流和播放、支持流媒体录制和转码等。Oryx是一个与SRS紧密相关的项目,它是一个基于SRS的流媒体解决方案,提供了一系列的工具和服务,帮助用户更方便地部署和管理流媒体服务。Oryx项目的目标是简化流媒体服务的配置和使用,使得即使是非技术背景的用户也能轻松上手。

全世界的博客 9828

荔枝派Lichee Nano全志F1C100s烧录避坑指南:从Fel模式到SPI Flash完整流程

本文详细介绍了荔枝派Lichee Nano基于全志F1C100s芯片的SPI Flash烧录完整流程,从FEL模式进入技巧到定制化sunxi-tools编译,再到分区策略与实战烧录步骤。针对开发者常见的烧录程序问题提供了专业解决方案,帮助用户高效完成嵌入式系统部署。

tea88的博客 788

银河麒麟(桌面版/服务器版)远程桌面配置全攻略:XRDP与VNC实战指南

本文提供了银河麒麟操作系统(桌面版与服务器版)配置远程桌面的完整实战指南。详细对比并演示了XRDP与VNC两种主流方案的安装、配置与连接步骤,涵盖从环境准备、软件安装、服务启动到客户端连接的全过程,并针对常见的黑屏、连接失败等问题给出了有效的排坑解决方案,帮助用户实现跨平台的便捷图形化远程管理。

b0c1d2的博客 812

SRS(simple-rtmp-server)流媒体服务器源码分析--系统启动

转载自 https://blog.youkuaiyun.com/ManagerUser/article/details/73840130 SRS(simple-rtmp-server)流媒体服务器源码分析--系统启动 一、前言 &nbsp; &nbsp; &nbsp; &nbsp;小卒最近看SRS源码,随手写...

qq_33349051的博客 543

ROS2理论与实践_宇树机器人Go2开发指南(文字)

本文介绍了Unitree Go2机器人ROS2开发环境的安装与配置流程。主要内容包括:1)从GitHub克隆unitree_ros2仓库;2)安装ROS2 Foxy版本依赖项;3)编译特定版本的CycloneDDS(0.10.2);4)编译unitree_go和unitree_api功能包;5)网络配置,包括设置静态IP和修改环境变量脚本setup.sh以指定网络接口。文章还提供了本地回环测试的替代方案,并建议配置完成后重启电脑。整个流程涉及ROS2环境切换、网络适配和编译安装等多个技术环节。

Li_Dongyi的博客 4206

SRS(simple-rtmp-server)流媒体服务器源码分析--HLS切片

一、前言 SRS流媒体服务器支持rtmp协议,但是rtmp协议仅仅支持PC直播。移动端直播需要HLS协议,HLS协议是由苹果公司发布,用于移动端视频直播,后来Android也对HLS做了友好支持。所以,SRS流媒体服务器支持rtmp协议和hls协议,满足了PC和移动端直播要求。 HLS协议有两个关键文件:.m3u8文件和.ts文件: .m3u8文件:播放控制文件,存...

ManagerUser的博客 1万+

AxMath行间公式转化为office公式求和符号以及编号显示问题

AxMath行间公式转化为office公式求和符号以及编号显示问题问题描述解决方法 问题描述 首先打出axmath公式,如下: 转换后的OfficeMath公式: 字体先不说,word公式字体默认cambria math且无法编辑。比较难受的一点是求和符号与后面的 ai+bi2a_i+b_i^2ai​+bi2​比例不匹配啊!正常的比例应该是: (虽然也不太正常,将就下) 解决方法 点击变换后的office公式右侧,选择更改为"显示" 可以看到,多了一些软回车符号(⬇),如果你想删除它,不要直接选中

weixin_43543177的博客 4653

高通SDX35:SGMII接口PHY RTL8211FSI-CG驱动适配

高通SDX35 RTL8211F SGMII

不秃头的程序猿Gabriel的博客 786

SRS流媒体服务器——Forward集群搭建和源码分析

Forward集群搭建和源码分析

m0_60259116的博客 2145

VxWorks系列 快速导航

Vxworks系列快速阅览导航

Mr.Sugarcane 1309

srs网络资源

官网: https://ossrs.net/srs.release/releases/ 官方GIT: ossrs/srs github SRS wiki SRS 1.0 Chinese SRS 2.0 Chinese SRS 3.0 Chinese 1. 曹同学专栏 (ManagerUser) . 全部文章> SRS SRS(simple-rtmp-...

machh的专栏 1093

YOLO26改进 - C2PSA | C2PSA融合DiffAttention差分注意力:轻量级差分计算实现高效特征降噪,提升模型抗干扰能力

本文介绍了 DiffCLIP,一种将差分注意力机制集成到 CLIP 架构的视觉 - 语言模型,并将其应用于 YOLO26。差分注意力机制通过计算两个互补注意力分布的差值,抵消无关信息干扰。单头差分注意力将 Q 和 K 拆分,分别计算注意力分布后做差值融合;多头差分注意力则每个头独立执行差分操作后聚合输出。关键参数 λ 可动态调控噪声抵消强度。我们将 DiffAttention 代码集成到 YOLO26 中,替换原有模块。实验表明,改进后的 YOLO26 在图像 - 文本理解任务中表现出色。

魔改工程师的博客 602

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

实时流媒体(语音聊天学习)

1. SRS(simple rtmp service) srs之深入浅出看流媒体 - 优快云博客 http://blog.youkuaiyun.com/zjqlovell/article/details/50786040 SRS产品规划 https://github.com/ossrs/srs/wiki/v1_CN_Product SRS2.0 home https://github.com/o...

liuxiaoxiaocsdn的博客 4102

C# WinForm 基于DirectShow.dll编写的程序,可边预览边保存视频,音频和图片

基于微软的DirectShow.dll的二次开发1.自动识别连接的摄像头跟麦克风且可以对其进行选择(默认选中第一个)2.可录制wmv和avi两种格式的视频并且都带有音频,avi可以录制的分辨率和帧数进行选择,wmv默认进行了淸晰度优化如不喜欢可去除代码中有相关提示3.可在预览点开之后进行拍照,也可边录制视频边拍照4.添加了视频和音频的压缩功能(酌情使用)这次二次开发发现C#对DirectShow的相关信息太少,这次做完也不敢藏私共享出来,还望大家指正

SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP信息Publish

前言        上一节中已经介绍了rtmp流监听,一旦有rtmp请求之后处理,单独为该链接开启一个线程等等,这一节我们来分析rtmp请求到来之后,除了创建线程之外,还要做哪些工作。同样,只是代码框架分析。 首先对Publish做的工作大概描述一下:       1、Rtmp协议几乎是从推送端过来的唯一协议(仅限于srs2.0及以下版本),因为Rtmp协议时效性。       2、建立R...

ManagerUser的博客 8286

pscad IEEE标准系统搭建说明

pscad IEEE标准系统搭建说明,包含典型的IEEE 9节点、14节点、30节点、39节点和118节点系统。内含标准测试系统的原始数据和潮流结果。

android 蓝牙通讯demo

android 下蓝牙通讯demo,实现了发现蓝牙,使能蓝牙,通过蓝牙发送消息。

srs源码分析1-搭建环境

在搭建srs-0.6.0时,总是提示各种错误,为了简化搭建,我直接使用CMake重新组织了一下构建方式,目前至只支持rtmp。 平台:Ubuntu 18.04 先安装state_threads: cd st make linux-debug cd ./obj cp libst.a /usr/local/lib/ cp st.h /usr/local/include/ 然后安装srs: mkdir build && cd build cmake ../ make 运行srs: ./srs

专注音视频基础、WebRTC、ffmpeg、流媒体服务器 545

simple rtmp server代码学习 (1)

最近从事一个视频流的项目开始了对simple rtmp server源代码的学习。

huade20的专栏 3008

windows环境下,搭建RTMP视频推流服务器

介绍如何在windows环境下,如何快速地搭建RTMP服务器

朱豪凯的博客 2万+

【亲测免费】 C++ RTMP Server:一款强大的开源RTMP流媒体服务器

C++ RTMP Server:一款强大的开源RTMP流媒体服务器 C++ RTMP Server(又称crtmpserver)是一个开源的RTMP流媒体服务器,主要使用C++编程语言开发。该项目旨在提供一个高性能、功能丰富的流媒体解决方案,适用于实时音视频传输场景。 项目的基础介绍 C++ RTMP Server基于GPL-3.0协议开源,项目托管在GitHub上,拥有活跃的开发者社区。该项目支...

gitblog_00241的博客 910

无需中转平台,通过视频解码器RTMP Server服务实现低延时传输

RTMP Server是RTMP/HLS流媒体服务器,通过RTMP Server服务功能可以将视频编码器可以直接将视频流通过RTMP传输到解码器进行解码和输出,无需RTMP中转平台。 【教程】如何通过视频解码器RTMP Server服务实现低延时传输? 传统业务模式下视频编码器直接推送RTMP网络流到视频解码器进行解码播放是不可以的,需要视频编码器推流RTMP到直播平台(或RTMP流媒体

千视KILOVIEW的博客 1879

SRS4.0源码分析-推流总结

本文采用的 SRS 版本是 4.0-b8 , 下载地址:github 本文主要对前面的文章做下总结。 《SRS4.0源码分析-main》,《SRS4.0源码分析-RTMP入口》,《SRS4.0源码分析-创建RTMP协程》,《SRS4.0源码分析-SrsRtmpConn::cycle》,《SRS4.0源码分析-SrsRtmpConn::stream_service_cycle》,《SRS4.0源码分析-SrsRecvThread::cycle》。 上面 7 篇 文章从 RTMP 入口,一直追踪到 推流数

u012117034的博客 2269

RTMP server 搭建的一种方法

  RTMP SERVER   ubuntu    16.04 LTS   nginx-rtmp-module   CPU:双核  内存:4G    带宽:5Mb/s    http://blog.youkuaiyun.com/kingroc/article/details/50839994    参考链接  is OK    http://summervast.blog.51cto.com/690507/3...

西山竹的博客 6558

SRS4.0源码分析-RTMP入口

本文采用的 SRS 版本是 4.0-b8 , 下载地址:github 上篇文章《SRS4.0源码分析-main》讲解了 SRS main 函数的基本流程,但是可能有些朋友还是比较懵逼。说实话,其实对于SRS的具体逻辑流程,我现在也是比较懵逼。在这里,分享一个研究开源项目源码的经验,怎么快速跳出这种懵逼的状态。 首先,研究一个项目的源码,需要有一个目标。例如之前《RTMP协议分析》的系列文章,已经讲解了一个RTMP客户端的实现,我研究 SRS 的源码就是为了看一下RTMP服务器端是如何实现的。...

u012117034的博客 2695
上一篇: SRS(simple-rtmp-server)流媒体服务器源码分析--RTMP信息Publish
下一篇: RTMP协议详解
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值