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

转载自 https://blog.csdn.net/ManagerUser/article/details/73840130

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()看起。

   
   
  1. int run_master()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. if ((ret = _srs_server->initialize_st()) != ERROR_SUCCESS) {
  5. return ret;
  6. }
  7. if ((ret = _srs_server->initialize_signal()) != ERROR_SUCCESS) {
  8. return ret;
  9. }
  10. //将pid进程号写进文件
  11. if ((ret = _srs_server->acquire_pid_file()) != ERROR_SUCCESS) {
  12. return ret;
  13. }
  14. //客户端监听
  15. if ((ret = _srs_server->listen()) != ERROR_SUCCESS) {
  16. return ret;
  17. }
  18. if ((ret = _srs_server->register_signal()) != ERROR_SUCCESS) {
  19. return ret;
  20. }
  21. if ((ret = _srs_server->http_handle()) != ERROR_SUCCESS) {
  22. return ret;
  23. }
  24. if ((ret = _srs_server->ingest()) != ERROR_SUCCESS) {
  25. return ret;
  26. }
  27. if ((ret = _srs_server->cycle()) != ERROR_SUCCESS) {
  28. return ret;
  29. }
  30. return 0;
  31. }

进入客户监听


   
   
  1. if ((ret = _srs_server->listen()) != ERROR_SUCCESS) {
  2. return ret;
  3. }
  监听内容:  不同的连接请求,有不同的监听

   
   
  1. int SrsServer::listen()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. // 创建一个rtmp的Streamlistener
  5. if ((ret = listen_rtmp()) != ERROR_SUCCESS) {
  6. return ret;
  7. }
  8. if ((ret = listen_http_api()) != ERROR_SUCCESS) {
  9. return ret;
  10. }
  11. if ((ret = listen_http_stream()) != ERROR_SUCCESS) {
  12. return ret;
  13. }
  14. if ((ret = listen_stream_caster()) != ERROR_SUCCESS) {
  15. return ret;
  16. }
  17. return ret;
  18. }

1、首先分析RTMP连接 


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

   
   
  1. // listen_rtmp 中listen监听走这里了。
  2. int SrsStreamListener::listen(string i, int p)
  3. {
  4. int ret = ERROR_SUCCESS;
  5. ip = i;
  6. port = p;
  7. srs_freep(listener);
  8. listener = new SrsTcpListener(this, ip, port);
  9. if ((ret = listener->listen()) != ERROR_SUCCESS) {
  10. srs_error("tcp listen failed. ret=%d", ret);
  11. return ret;
  12. }
  13. srs_info("listen thread current_cid=%d, "
  14. "listen at port=%d, type=%d, fd=%d started success, ep=%s:%d",
  15. _srs_context->get_id(), p, type, listener->fd(), i.c_str(), p);
  16. srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd());
  17. return ret;
  18. }
注意,这里有大量纯虚函数,不要走错路了。进入TCP监听代码

   
   
  1. // rtmp tcp监听
  2. int SrsTcpListener::listen()
  3. {
  4. //C++ Socket编程
  5. int ret = ERROR_SUCCESS;
  6. // 1、创建套接字,流式Socket(SOCK_STREAM)
  7. if ((_fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  8. ret = ERROR_SOCKET_CREATE;
  9. srs_error("create linux socket error. port=%d, ret=%d", port, ret);
  10. return ret;
  11. }
  12. srs_verbose("create linux socket success. port=%d, fd=%d", port, _fd);
  13. int reuse_socket = 1;
  14. if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
  15. ret = ERROR_SOCKET_SETREUSE;
  16. srs_error("setsockopt reuse-addr error. port=%d, ret=%d", port, ret);
  17. return ret;
  18. }
  19. srs_verbose("setsockopt reuse-addr success. port=%d, fd=%d", port, _fd);
  20. sockaddr_in addr;
  21. addr.sin_family = AF_INET;
  22. addr.sin_port = htons(port);
  23. addr.sin_addr.s_addr = inet_addr(ip.c_str());
  24. // 2、绑定套接字到一个IP地址和一个端口上
  25. if (bind(_fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
  26. ret = ERROR_SOCKET_BIND;
  27. srs_error("bind socket error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
  28. return ret;
  29. }
  30. srs_verbose("bind socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
  31. // 3、将套接字设置为监听模式等待连接请求
  32. if (::listen(_fd, SERVER_LISTEN_BACKLOG) == -1) {
  33. ret = ERROR_SOCKET_LISTEN;
  34. srs_error("listen socket error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
  35. return ret;
  36. }
  37. srs_verbose("listen socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
  38. if ((_stfd = st_netfd_open_socket(_fd)) == NULL){
  39. ret = ERROR_ST_OPEN_SOCKET;
  40. srs_error("st_netfd_open_socket open socket failed. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
  41. return ret;
  42. }
  43. srs_verbose("st open socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
  44. // 4、等到连接一个客户之后,开启一个新的线程
  45. if ((ret = pthread->start()) != ERROR_SUCCESS) {
  46. srs_error("st_thread_create listen thread error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
  47. return ret;
  48. }
  49. srs_verbose("create st listen thread success, ep=%s:%d", ip.c_str(), port);
  50. return ret;
  51. }
       此代码为C++ TCP  Socket代码,思路比较清晰,可以看到,每接受到一个rtmp访问请求,创建一个”线程“,这里暂时将其称为线程,后面再做具体介绍。创建线程代码如下:

   
   
  1. int SrsReusableThread::start()
  2. {
  3. return pthread->start();
  4. }


   
   
  1. int SrsThread::start()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. if(tid) {
  5. srs_info( "thread %s already running.", _name);
  6. return ret;
  7. }
  8. if((tid = st_thread_create(thread_fun, this, (_joinable? 1: 0), 0)) == NULL){
  9. ret = ERROR_ST_CREATE_CYCLE_THREAD;
  10. srs_error( "st_thread_create failed. ret=%d", ret);
  11. return ret;
  12. }
  13. disposed = false;
  14. // we set to loop to true for thread to run.
  15. loop = true;
  16. // wait for cid to ready, for parent thread to get the cid.
  17. while (_cid < 0) {
  18. st_usleep( 10 * 1000);
  19. }
  20. // now, cycle thread can run.
  21. can_run = true;
  22. return ret;
  23. }

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

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

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


    
    
  1. // 每连链接一个用户,创建一个协程程,该函数为协程函数
  2. void* SrsThread::thread_fun(void* arg)
  3. {
  4. SrsThread* obj = (SrsThread*)arg;
  5. srs_assert(obj);
  6. // 进入线程循环
  7. obj->thread_cycle();
  8. // for valgrind to detect.
  9. SrsThreadContext* ctx = dynamic_cast <SrsThreadContext*>(_srs_context);
  10. if (ctx) {
  11. ctx->clear_cid();
  12. }
  13. st_thread_exit(NULL);
  14. return NULL;
  15. }
此时,真正进入了协程循环处理

    
    
  1. void SrsThread::thread_cycle()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. _srs_context->generate_id();
  5. srs_info("thread %s cycle start", _name);
  6. _cid = _srs_context->get_id();
  7. srs_assert(handler);
  8. handler->on_thread_start();
  9. // thread is running now.
  10. really_terminated = false;
  11. // wait for cid to ready, for parent thread to get the cid.
  12. while (!can_run && loop) {
  13. st_usleep(10 * 1000);
  14. }
  15. while (loop) {
  16. if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
  17. srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", _name, ret);
  18. goto failed;
  19. }
  20. srs_info("thread %s on before cycle success", _name);
  21. // 注意纯虚函数的应用
  22. if ((ret = handler->cycle()) != ERROR_SUCCESS) {
  23. if (!srs_is_client_gracefully_close(ret) && !srs_is_system_control_error(ret)) {
  24. srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);
  25. }
  26. goto failed;
  27. }
  28. srs_info("thread %s cycle success", _name);
  29. if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
  30. srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", _name, ret);
  31. goto failed;
  32. }
  33. srs_info("thread %s on end cycle success", _name);
  34. failed:
  35. if (!loop) {
  36. break;
  37. }
  38. // to improve performance, donot sleep when interval is zero.
  39. // @see: https://github.com/ossrs/srs/issues/237
  40. if (cycle_interval_us != 0) {
  41. st_usleep(cycle_interval_us);
  42. }
  43. }
  44. // readly terminated now.
  45. really_terminated = true;
  46. handler->on_thread_stop();
  47. srs_info("thread %s cycle finished", _name);
  48. }
       至此,一定要熟悉C++纯虚函数的引用,本人刚学了几天C++,对虚函数和纯虚函数在SRS源码中的应用很不习惯!  好了,进入循环ret = handler->cycle()

    
    
  1. int SrsConnection::cycle()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. _srs_context->generate_id();
  5. id = _srs_context->get_id();
  6. ip = srs_get_peer_ip(st_netfd_fileno(stfd));
  7. //srs_trace("ip:%s", ip);
  8. ret = do_cycle();
  9. // if socket io error, set to closed.
  10. if (srs_is_client_gracefully_close(ret)) {
  11. ret = ERROR_SOCKET_CLOSED;
  12. }
  13. // success.
  14. if (ret == ERROR_SUCCESS) {
  15. srs_trace("client finished.");
  16. }
  17. // client close peer.
  18. if (ret == ERROR_SOCKET_CLOSED) {
  19. srs_warn("client disconnect peer. ret=%d", ret);
  20. }
  21. return ERROR_SUCCESS;
  22. }
进入ret=do_cycle();

    
    
  1. // TODO: return detail message when error for client.
  2. int SrsRtmpConn::do_cycle()
  3. {
  4. int ret = ERROR_SUCCESS;
  5. srs_trace( "RTMP client ip=%s", ip.c_str());
  6. rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
  7. rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);
  8. if ((ret = rtmp->handshake()) != ERROR_SUCCESS) {
  9. srs_error( "rtmp handshake failed. ret=%d", ret);
  10. return ret;
  11. }
  12. srs_verbose( "rtmp handshake success");
  13. if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) {
  14. srs_error( "rtmp connect vhost/app failed. ret=%d", ret);
  15. return ret;
  16. }
  17. srs_verbose( "rtmp connect app success");
  18. // set client ip to request.
  19. req->ip = ip;
  20. // discovery vhost, resolve the vhost from config
  21. SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
  22. if (parsed_vhost) {
  23. req->vhost = parsed_vhost->arg0();
  24. }
  25. srs_info( "discovery app success. schema=%s, vhost=%s, port=%s, app=%s",
  26. req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str());
  27. if (req->schema.empty() || req->vhost.empty() || req->port.empty() || req->app.empty()) {
  28. ret = ERROR_RTMP_REQ_TCURL;
  29. srs_error( "discovery tcUrl failed. "
  30. "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d",
  31. req->tcUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str(), ret);
  32. return ret;
  33. }
  34. // check vhost
  35. if ((ret = check_vhost()) != ERROR_SUCCESS) {
  36. srs_error( "check vhost failed. ret=%d", ret);
  37. return ret;
  38. }
  39. srs_verbose( "check vhost success.");
  40. srs_trace( "connect app, "
  41. "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, args=%s",
  42. req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),
  43. req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),
  44. req->app.c_str(), (req->args? "(obj)": "null"));
  45. // show client identity
  46. if(req->args) {
  47. std:: string srs_version;
  48. std:: string srs_server_ip;
  49. int srs_pid = 0;
  50. int srs_id = 0;
  51. SrsAmf0Any* prop = NULL;
  52. if ((prop = req->args->ensure_property_string( "srs_version")) != NULL) {
  53. srs_version = prop->to_str();
  54. }
  55. if ((prop = req->args->ensure_property_string( "srs_server_ip")) != NULL) {
  56. srs_server_ip = prop->to_str();
  57. }
  58. if ((prop = req->args->ensure_property_number( "srs_pid")) != NULL) {
  59. srs_pid = ( int)prop->to_number();
  60. }
  61. if ((prop = req->args->ensure_property_number( "srs_id")) != NULL) {
  62. srs_id = ( int)prop->to_number();
  63. }
  64. srs_info( "edge-srs ip=%s, version=%s, pid=%d, id=%d",
  65. srs_server_ip.c_str(), srs_version.c_str(), srs_pid, srs_id);
  66. if (srs_pid > 0) {
  67. srs_trace( "edge-srs ip=%s, version=%s, pid=%d, id=%d",
  68. srs_server_ip.c_str(), srs_version.c_str(), srs_pid, srs_id);
  69. }
  70. }
  71. ret = service_cycle();
  72. http_hooks_on_close();
  73. return ret;
  74. }
行了,rtmp连接就到这里,要不然都快到rtmp流接受代码了,和系统启动越走越远了,rtmp流接受后面再分析。


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


    
    
  1. int SrsServer::listen_http_api()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. #ifdef SRS_AUTO_HTTP_API
  5. close_listeners(SrsListenerHttpApi);
  6. if (_srs_config->get_http_api_enabled()) {
  7. SrsListener* listener = new SrsStreamListener(this, SrsListenerHttpApi);
  8. listeners.push_back(listener);
  9. std::string ep = _srs_config->get_http_api_listen();
  10. std::string ip;
  11. int port;
  12. srs_parse_endpoint(ep, ip, port);
  13. if ((ret = listener->listen(ip, port)) != ERROR_SUCCESS) {
  14. srs_error("HTTP api listen at %s:%d failed. ret=%d", ip.c_str(), port, ret);
  15. return ret;
  16. }
  17. }
  18. #endif
  19. return ret;
  20. }
listen_http_api()函数和listen_rtmp()函数内容非常像,再走到listener->listen()里面看看,结果来到了

    
    
  1. int SrsStreamListener::listen(string i, int p)
  2. {
  3. int ret = ERROR_SUCCESS;
  4. ip = i;
  5. port = p;
  6. srs_freep(listener);
  7. listener = new SrsTcpListener(this, ip, port);
  8. if ((ret = listener->listen()) != ERROR_SUCCESS) {
  9. srs_error("tcp listen failed. ret=%d", ret);
  10. return ret;
  11. }
  12. srs_info("listen thread current_cid=%d, "
  13. "listen at port=%d, type=%d, fd=%d started success, ep=%s:%d",
  14. _srs_context->get_id(), p, type, listener->fd(), i.c_str(), p);
  15. srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd());
  16. return ret;
  17. }
和rtmp链接监听机制完全一样,只是type不同而已

    
    
  1. enum SrsListenerType
  2. {
  3. // RTMP client,
  4. SrsListenerRtmpStream = 0,
  5. // HTTP api,
  6. SrsListenerHttpApi = 1,
  7. // HTTP stream, HDS/HLS/DASH
  8. SrsListenerHttpStream = 2,
  9. // UDP stream, MPEG-TS over udp.
  10. SrsListenerMpegTsOverUdp = 3,
  11. // TCP stream, RTSP stream.
  12. SrsListenerRtsp = 4,
  13. // TCP stream, FLV stream over HTTP.
  14. SrsListenerFlv = 5,
  15. };
我就看了两个链接监听,监听到此为止。

3、http api回调注册

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


    
    
  1. int SrsServer::http_handle()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. #ifdef SRS_AUTO_HTTP_API
  5. srs_assert(http_api_mux);
  6. if ((ret = http_api_mux->handle("/", new SrsHttpNotFoundHandler())) != ERROR_SUCCESS) {
  7. return ret;
  8. }
  9. if ((ret = http_api_mux->handle("/api/", new SrsGoApiApi())) != ERROR_SUCCESS) {
  10. return ret;
  11. }
  12. if ((ret = http_api_mux->handle("/api/v1/", new SrsGoApiV1())) != ERROR_SUCCESS) {
  13. return ret;
  14. }
  15. if ((ret = http_api_mux->handle("/api/v1/versions", new SrsGoApiVersion())) != ERROR_SUCCESS) {
  16. return ret;
  17. }
  18. if ((ret = http_api_mux->handle("/api/v1/summaries", new SrsGoApiSummaries())) != ERROR_SUCCESS) {
  19. return ret;
  20. }
  21. if ((ret = http_api_mux->handle("/api/v1/rusages", new SrsGoApiRusages())) != ERROR_SUCCESS) {
  22. return ret;
  23. }
  24. if ((ret = http_api_mux->handle("/api/v1/self_proc_stats", new SrsGoApiSelfProcStats())) != ERROR_SUCCESS) {
  25. return ret;
  26. }
  27. if ((ret = http_api_mux->handle("/api/v1/system_proc_stats", new SrsGoApiSystemProcStats())) != ERROR_SUCCESS) {
  28. return ret;
  29. }
  30. if ((ret = http_api_mux->handle("/api/v1/meminfos", new SrsGoApiMemInfos())) != ERROR_SUCCESS) {
  31. return ret;
  32. }
  33. if ((ret = http_api_mux->handle("/api/v1/authors", new SrsGoApiAuthors())) != ERROR_SUCCESS) {
  34. return ret;
  35. }
  36. if ((ret = http_api_mux->handle("/api/v1/features", new SrsGoApiFeatures())) != ERROR_SUCCESS) {
  37. return ret;
  38. }
  39. if ((ret = http_api_mux->handle("/api/v1/vhosts/", new SrsGoApiVhosts())) != ERROR_SUCCESS) {
  40. return ret;
  41. }
  42. if ((ret = http_api_mux->handle("/api/v1/streams/", new SrsGoApiStreams())) != ERROR_SUCCESS) {
  43. return ret;
  44. }
  45. if ((ret = http_api_mux->handle("/api/v1/clients/", new SrsGoApiClients())) != ERROR_SUCCESS) {
  46. return ret;
  47. }
  48. // test the request info.
  49. if ((ret = http_api_mux->handle("/api/v1/tests/requests", new SrsGoApiRequests())) != ERROR_SUCCESS) {
  50. return ret;
  51. }
  52. // test the error code response.
  53. if ((ret = http_api_mux->handle("/api/v1/tests/errors", new SrsGoApiError())) != ERROR_SUCCESS) {
  54. return ret;
  55. }
  56. // test the redirect mechenism.
  57. if ((ret = http_api_mux->handle("/api/v1/tests/redirects", new SrsHttpRedirectHandler("/api/v1/tests/errors", SRS_CONSTS_HTTP_MovedPermanently))) != ERROR_SUCCESS) {
  58. return ret;
  59. }
  60. // test the http vhost.
  61. if ((ret = http_api_mux->handle("error.srs.com/api/v1/tests/errors", new SrsGoApiError())) != ERROR_SUCCESS) {
  62. return ret;
  63. }
  64. // TODO: FIXME: for console.
  65. // TODO: FIXME: support reload.
  66. std::string dir = _srs_config->get_http_stream_dir() + "/console";
  67. if ((ret = http_api_mux->handle("/console/", new SrsHttpFileServer(dir))) != ERROR_SUCCESS) {
  68. srs_error("http: mount console dir=%s failed. ret=%d", dir.c_str(), ret);
  69. return ret;
  70. }
  71. srs_trace("http: api mount /console to %s", dir.c_str());
  72. #endif
  73. return ret;
  74. }

该函数注册了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代码只是实现简单的系统调用,这部分内容在后面的章节中详细说明。


    
    
  1. int SrsIngester::start()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. if ((ret = parse()) != ERROR_SUCCESS) {
  5. clear_engines();
  6. ret = ERROR_SUCCESS;
  7. return ret;
  8. }
  9. // even no ingesters, we must also start it,
  10. // for the reload may add more ingesters.
  11. // start thread to run all encoding engines.
  12. if ((ret = pthread->start()) != ERROR_SUCCESS) {
  13. srs_error("st_thread_create failed. ret=%d", ret);
  14. return ret;
  15. }
  16. srs_trace("ingest thread cid=%d, current_cid=%d", pthread->cid(), _srs_context->get_id());
  17. return ret;
  18. }
到此,可以看出,和监听过程一样,进入int SrsThread::start()函数,只是传入对象不一样而已。

5、SRS自服务



    
    
  1. int SrsServer::cycle()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. srs_trace("SrsServer")
  5. ret = do_cycle();
  6. #ifdef SRS_AUTO_GPERF_MC
  7. destroy();
  8. // remark, for gmc, never invoke the exit().
  9. srs_warn("sleep a long time for system st-threads to cleanup.");
  10. st_usleep(3 * 1000 * 1000);
  11. srs_warn("system quit");
  12. #else
  13. // normally quit with neccessary cleanup by dispose().
  14. srs_warn("main cycle terminated, system quit normally.");
  15. dispose();
  16. srs_trace("srs terminated");
  17. // for valgrind to detect.
  18. srs_freep(_srs_config);
  19. srs_freep(_srs_log);
  20. exit(0);
  21. #endif
  22. return ret;
  23. }

    
    
  1. int SrsServer::do_cycle()
  2. {
  3. int ret = ERROR_SUCCESS;
  4. // find the max loop
  5. int max = srs_max(0, SRS_SYS_TIME_RESOLUTION_MS_TIMES);
  6. #ifdef SRS_AUTO_STAT
  7. max = srs_max(max, SRS_SYS_RUSAGE_RESOLUTION_TIMES);
  8. max = srs_max(max, SRS_SYS_CPU_STAT_RESOLUTION_TIMES);
  9. max = srs_max(max, SRS_SYS_DISK_STAT_RESOLUTION_TIMES);
  10. max = srs_max(max, SRS_SYS_MEMINFO_RESOLUTION_TIMES);
  11. max = srs_max(max, SRS_SYS_PLATFORM_INFO_RESOLUTION_TIMES);
  12. max = srs_max(max, SRS_SYS_NETWORK_DEVICE_RESOLUTION_TIMES);
  13. max = srs_max(max, SRS_SYS_NETWORK_RTMP_SERVER_RESOLUTION_TIMES);
  14. #endif
  15. // for asprocess.
  16. bool asprocess = _srs_config->get_asprocess();
  17. // the deamon thread, update the time cache
  18. while (true) {
  19. if(handler && (ret = handler->on_cycle((int)conns.size())) != ERROR_SUCCESS){
  20. srs_error("cycle handle failed. ret=%d", ret);
  21. return ret;
  22. }
  23. // the interval in config.
  24. int heartbeat_max_resolution = (int)(_srs_config->get_heartbeat_interval() / SRS_SYS_CYCLE_INTERVAL);
  25. // dynamic fetch the max.
  26. int temp_max = max;
  27. temp_max = srs_max(temp_max, heartbeat_max_resolution);
  28. for (int i = 0; i < temp_max; i++) {
  29. st_usleep(SRS_SYS_CYCLE_INTERVAL * 1000);
  30. // asprocess check.
  31. if (asprocess && ::getppid() != ppid) {
  32. srs_warn("asprocess ppid changed from %d to %d", ppid, ::getppid());
  33. return ret;
  34. }
  35. // gracefully quit for SIGINT or SIGTERM.
  36. if (signal_gracefully_quit) {
  37. srs_trace("cleanup for gracefully terminate.");
  38. return ret;
  39. }
  40. // for gperf heap checker,
  41. // @see: research/gperftools/heap-checker/heap_checker.cc
  42. // if user interrupt the program, exit to check mem leak.
  43. // but, if gperf, use reload to ensure main return normally,
  44. // because directly exit will cause core-dump.
  45. #ifdef SRS_AUTO_GPERF_MC
  46. if (signal_gmc_stop) {
  47. srs_warn("gmc got singal to stop server.");
  48. return ret;
  49. }
  50. #endif
  51. // do reload the config.
  52. if (signal_reload) {
  53. signal_reload = false;
  54. srs_info("get signal reload, to reload the config.");
  55. if ((ret = _srs_config->reload()) != ERROR_SUCCESS) {
  56. srs_error("reload config failed. ret=%d", ret);
  57. return ret;
  58. }
  59. srs_trace("reload config success.");
  60. }
  61. // notice the stream sources to cycle.
  62. if ((ret = SrsSource::cycle_all()) != ERROR_SUCCESS) {
  63. return ret;
  64. }
  65. // update the cache time
  66. if ((i % SRS_SYS_TIME_RESOLUTION_MS_TIMES) == 0) {
  67. srs_info("update current time cache.");
  68. srs_update_system_time_ms();
  69. }
  70. #ifdef SRS_AUTO_STAT
  71. if ((i % SRS_SYS_RUSAGE_RESOLUTION_TIMES) == 0) {
  72. srs_info("update resource info, rss.");
  73. srs_update_system_rusage();
  74. }
  75. if ((i % SRS_SYS_CPU_STAT_RESOLUTION_TIMES) == 0) {
  76. srs_info("update cpu info, cpu usage.");
  77. srs_update_proc_stat();
  78. }
  79. if ((i % SRS_SYS_DISK_STAT_RESOLUTION_TIMES) == 0) {
  80. srs_info("update disk info, disk iops.");
  81. srs_update_disk_stat();
  82. }
  83. if ((i % SRS_SYS_MEMINFO_RESOLUTION_TIMES) == 0) {
  84. srs_info("update memory info, usage/free.");
  85. srs_update_meminfo();
  86. }
  87. if ((i % SRS_SYS_PLATFORM_INFO_RESOLUTION_TIMES) == 0) {
  88. srs_info("update platform info, uptime/load.");
  89. srs_update_platform_info();
  90. }
  91. if ((i % SRS_SYS_NETWORK_DEVICE_RESOLUTION_TIMES) == 0) {
  92. srs_info("update network devices info.");
  93. srs_update_network_devices();
  94. }
  95. if ((i % SRS_SYS_NETWORK_RTMP_SERVER_RESOLUTION_TIMES) == 0) {
  96. srs_info("update network server kbps info.");
  97. resample_kbps();
  98. }
  99. #ifdef SRS_AUTO_HTTP_CORE
  100. if (_srs_config->get_heartbeat_enabled()) {
  101. if ((i % heartbeat_max_resolution) == 0) {
  102. srs_info("do http heartbeat, for internal server to report.");
  103. http_heartbeat->heartbeat();
  104. }
  105. }
  106. #endif
  107. #endif
  108. srs_info("server main thread loop");
  109. }
  110. }
  111. return ret;
  112. }
主线程,更新srs时间和缓存!!至此,系统启动代码结构梳理完了。

三、总结

  • 启动不同的业务。
  • 监听不同的客户端类型。
  • 每链接一个客户端,SRS为其创建一个协程,专门负责该路链接信息交互。
  • SRS系统采用了协程网络服务框架,使得系统具有高并发,高性能等有点。
转载自 https://blog.csdn.net/ManagerUser/article/details/73840130

猜你喜欢

转载自blog.csdn.net/qq_33349051/article/details/84872323
今日推荐