Realization of srt protocol srs server for streaming media analysis

This article is based on srs 5.0 analysis:

1. Start the listener and create a SrsMpegtsSrtConn object for each srt push-pull stream:

   The srt service is started in the function: run_hybrid_server; create the SrsSrtServerAdapter service and execute the SrsSrtServerAdapter::run function.

srs_error_t run_hybrid_server(void* /*arg*/)
{
    srs_error_t err = srs_success;

    // Create servers and register them.
    _srs_hybrid->register_server(new SrsServerAdapter());

#ifdef SRS_SRT
    _srs_hybrid->register_server(new SrsSrtServerAdapter());   // 启动srt 服务
#endif

#ifdef SRS_RTC
    _srs_hybrid->register_server(new RtcServerAdapter());
#endif

    // Do some system initialize.
    if ((err = _srs_hybrid->initialize()) != srs_success) {
        return srs_error_wrap(err, "hybrid initialize");
    }

    // Circuit breaker to protect server, which depends on hybrid.
    if ((err = _srs_circuit_breaker->initialize()) != srs_success) {
        return srs_error_wrap(err, "init circuit breaker");
    }

    // Should run util hybrid servers all done.
    if ((err = _srs_hybrid->run()) != srs_success) {
        return srs_error_wrap(err, "hybrid run");
    }

    // After all done, stop and cleanup.
    _srs_hybrid->stop();

    return err;
}

   SrsSrtServerAdapter::run analysis: Create a SrsSrtServer object and execute SrsSrtServer::listen()

srs_error_t SrsSrtServerAdapter::run(SrsWaitGroup* wg)
{
    srs_error_t err = srs_success;

    // Initialize the whole system, set hooks to handle server level events.
    if ((err = srt_server_->initialize()) != srs_success) {
        return srs_error_wrap(err, "srt server initialize");
    }

    if ((err = srt_server_->listen()) != srs_success) {    //srt_server_ = new SrsSrtServer(); 
        return srs_error_wrap(err, "srt listen");
    }

    return err;
}

 srs_error_t SrsSrtServer::listen() function calls listen_srt_mpegts; listen_srt_mpegts creates a SrsSrtAcceptor object and executes SrsSrtAcceptor::listen

srs_error_t SrsSrtServer::listen_srt_mpegts()
{
    srs_error_t err = srs_success;

    if (! _srs_config->get_srt_enabled()) {
        return err;
    }

    // Close all listener for SRT if exists.
    close_listeners();

    // Start a listener for SRT, we might need multiple listeners in the future.
    SrsSrtAcceptor* acceptor = new SrsSrtAcceptor(this);
    acceptors_.push_back(acceptor);

    int port; string ip;
    srs_parse_endpoint(srs_int2str(_srs_config->get_srt_listen_port()), ip, port);

    if ((err = acceptor->listen(ip, port)) != srs_success) {       //acceptor = new SrsSrtAcceptor(this);
        return srs_error_wrap(err, "srt listen %s:%d", ip.c_str(), port);
    }

    return err;
	
}

 SrsSrtAcceptor::listen function: create SrsSrtListener object, and transfer SrsSrtAcceptor class object, execute: SrsSrtListener::listen function.

srs_error_t SrsSrtAcceptor::listen(std::string ip, int port)
{
    srs_error_t err = srs_success;

    ip_ = ip;
    port_ = port;

    srs_freep(listener_);
    listener_ = new SrsSrtListener(this, ip_, port_);    // 创建SrsSrtListener 对象,并传输SrsSrtAcceptor 类对象

    // Create srt socket.
    if ((err = listener_->create_socket()) != srs_success) {
        return srs_error_wrap(err, "message srt acceptor");
    }

    // Set all the srt option from config.
    if ((err = set_srt_opt()) != srs_success) {
        return srs_error_wrap(err, "set opt");
    }

    // Start listen srt socket, this function will set the socket in async mode.
    if ((err = listener_->listen()) != srs_success) {    //  listener_ = new SrsSrtListener
        return srs_error_wrap(err, "message srt acceptor");
    }

    srs_trace("srt listen at udp://%s:%d, fd=%d", ip_.c_str(), port_, listener_->fd());

    return err;
}

  SrsSrtListener::listen function: Create a SrsSTCoroutine object and execute SrsSTCoroutine::start

srs_error_t SrsSrtListener::listen()
{
    srs_error_t err = srs_success;

    if ((err = srs_srt_listen(lfd_, ip_, port_)) != srs_success) {
        return srs_error_wrap(err, "srs_srt_listen");
    }

    srt_skt_ = new SrsSrtSocket(_srt_eventloop->poller(), lfd_);
    // Accept never timeout.
    srt_skt_->set_recv_timeout(SRS_UTIME_NO_TIMEOUT);
    srt_skt_->set_send_timeout(SRS_UTIME_NO_TIMEOUT);
    
    srs_freep(trd_);
    trd_ = new SrsSTCoroutine("srt_listener", this);    // this 指向SrsSrtListener
    if ((err = trd_->start()) != srs_success) {       // trd_ = new SrsSTCoroutine  
        return srs_error_wrap(err, "start coroutine");  
    }
    
    return err;
}

 srs_error_t SrsSTCoroutine::start()
{
    return impl_->start();       // impl_ = new SrsFastCoroutine(n, h);
}

SrsFastCoroutine::start : Create a coroutine and execute SrsFastCoroutine::cycle ; where the function executes handler->cycle

srs_error_t SrsFastCoroutine::start()
{
    srs_error_t err = srs_success;
    
    if (started || disposed) {
        if (disposed) {
            err = srs_error_new(ERROR_THREAD_DISPOSED, "disposed");
        } else {
            err = srs_error_new(ERROR_THREAD_STARTED, "started");
        }

        if (trd_err == srs_success) {
            trd_err = srs_error_copy(err);
        }
        
        return err;
    }

    if ((trd = (srs_thread_t)_pfn_st_thread_create(pfn, this, 1, stack_size)) == NULL) {    创建协程  //void* SrsFastCoroutine::pfn(void* arg)
        err = srs_error_new(ERROR_ST_CREATE_CYCLE_THREAD, "create failed");
        
        srs_freep(trd_err);
        trd_err = srs_error_copy(err);
        
        return err;
    }
    
    started = true;

    return err;
}


void* SrsFastCoroutine::pfn(void* arg)
{
    SrsFastCoroutine* p = (SrsFastCoroutine*)arg;

    srs_error_t err = p->cycle();            // SrsFastCoroutine::cycle()

    // Set the err for function pull to fetch it.
    // @see https://github.com/ossrs/srs/pull/1304#issuecomment-480484151
    if (err != srs_success) {
        srs_freep(p->trd_err);
        // It's ok to directly use it, because it's returned by st_thread_join.
        p->trd_err = err;
    }

    return (void*)err;
}


srs_error_t SrsFastCoroutine::cycle()
{
    if (_srs_context) {
        if (cid_.empty()) {
            cid_ = _srs_context->generate_id();
        }
        _srs_context->set_id(cid_);
    }
    
    srs_error_t err = handler->cycle();     // 
    if (err != srs_success) {
        return srs_error_wrap(err, "coroutine cycle");
    }

    // Set cycle done, no need to interrupt it.
    cycle_done = true;
    
    return err;
}

SrsFastCoroutine class constructor: hander points to SrsSrtListener, handler->cycle actually calls SrsSrtListener::cycle. SrsSrtListener::cycle function calls handler_->on_srt_client

rsFastCoroutine::SrsFastCoroutine(string n, ISrsCoroutineHandler* h)
{
    // TODO: FIXME: Reduce duplicated code.
    name = n;
    handler = h;         // handler        指向SrsSrtListener
    trd = NULL;
    trd_err = srs_success;
    started = interrupted = disposed = cycle_done = false;
    stopping_ = false;

    //  0 use default, default is 64K.
    stack_size = 0;
}


srs_error_t SrsSrtListener::cycle()
{
    srs_error_t err = srs_success;
    
    while (true) {
        if ((err = trd_->pull()) != srs_success) {
            return srs_error_wrap(err, "srt listener");
        }
        
        srs_srt_t client_srt_fd = srs_srt_socket_invalid();
        if ((err = srt_skt_->accept(&client_srt_fd)) != srs_success) {
            return srs_error_wrap(err, "srt accept");
        }
        
        // TODO: FIXME: print some log and client srt options.

        if ((err = handler_->on_srt_client(client_srt_fd)) != srs_success) {    //handler 指向 SrsSrtAcceptor
            return srs_error_wrap(err, "handle srt fd=%d", client_srt_fd);
        }
    }
    
    return err;
}

handler_ points to SrsSrtAcceptor, handler_->on_srt_client actually calls SrsSrtAcceptor::on_srt_client,

SrsSrtAcceptor::on_srt_client function calls srt_server_->accept_srt_client

srs_error_t SrsSrtAcceptor::on_srt_client(srs_srt_t srt_fd)
{
    srs_error_t err = srs_success;

    // Notify srt server to accept srt client, and create new SrsSrtConn on it.
    if ((err = srt_server_->accept_srt_client(srt_fd)) != srs_success) {
        srs_warn("accept srt client failed, err is %s", srs_error_desc(err).c_str());
        srs_freep(err);
    }
    
    return err;
}

srt_server_->accept_srt_client 指向SrsSrtServer::accept_srt_client:

The main function of SrsSrtServer::accept_srt_client function is to create SrsMpegtsSrtConn for each srt push-pull stream, and execute SrsMpegtsSrtConn::start. So far, the srt monitoring service function is completed.

srs_error_t SrsSrtServer::accept_srt_client(srs_srt_t srt_fd)
{
    srs_error_t err = srs_success;

    ISrsResource* resource = NULL;
    if ((err = fd_to_resource(srt_fd, &resource)) != srs_success) {   //fd_to_resource  创建 new SrsMpegtsSrtConn(this, srt_fd, ip, port)
        //close fd on conn error, otherwise will lead to fd leak -gs
        // TODO: FIXME: Handle error.
        srs_srt_close(srt_fd);
        return srs_error_wrap(err, "srt fd to resource");
    }
    srs_assert(resource);
    
    // directly enqueue, the cycle thread will remove the client.
    conn_manager_->add(resource);

    ISrsStartable* conn = dynamic_cast<ISrsStartable*>(resource);
    if ((err = conn->start()) != srs_success) {         //  conn 指向SrsMpegtsSrtConn
        return srs_error_wrap(err, "start srt conn coroutine");
    }
    
    return err;
}

2. SrsMpegtsSrtConn streaming service processing:

  Pass SrsSTCoroutine, the coroutine passes in the SrsMpegtsSrtConn object, and the final coroutine execution function is SrsMpegtsSrtConn::cycle(); SrsMpegtsSrtConn::cycle() function calls the do_cycle function: do_cycle

The do_cycle function is distinguished according to the type of SrsMpegtsSrtConn object: push stream executes publishing; pull stream executes playing processing.

srs_error_t SrsMpegtsSrtConn::start()
{
    srs_error_t err = srs_success;

    if ((err = trd_->start()) != srs_success) {      //trd_= new SrsSTCoroutine("ts-srt", this, _srs_context->get_id()) 创建协程,最终执行SrsMpegtsSrtConn::cycle()
        return srs_error_wrap(err, "coroutine");
    }

    return err;
}


srs_error_t SrsMpegtsSrtConn::cycle()
{
    srs_error_t err = do_cycle();

    // Update statistic when done.
    SrsStatistic* stat = SrsStatistic::instance();
    stat->kbps_add_delta(get_id().c_str(), delta_);
    stat->on_disconnect(get_id().c_str());

    // Notify manager to remove it.
    // Note that we create this object, so we use manager to remove it.
    srt_server_->remove(this);

    // success.
    if (err == srs_success) {
        srs_trace("srt client finished.");
        return err;
    }

    srs_error("srt serve error %s", srs_error_desc(err).c_str());
    srs_freep(err);
    return srs_success;
}
srs_error_t SrsMpegtsSrtConn::do_cycle()
{
    srs_error_t err = srs_success;

    srs_trace("SRT client ip=%s:%d, fd=%d", ip_.c_str(), port_, srt_fd_);

    string streamid = "";
    if ((err = srs_srt_get_streamid(srt_fd_, streamid)) != srs_success) {
        return srs_error_wrap(err, "get srt streamid");
    }

    // Must have streamid, because srt ts packet will convert to rtmp or rtc.
    if (streamid.empty()) {
        return srs_error_new(ERROR_SRT_CONN, "empty srt streamid");
    }

    // Detect streamid of srt to request.
    SrtMode mode = SrtModePull;
    if (!srs_srt_streamid_to_request(streamid, mode, req_)) {
        return srs_error_new(ERROR_SRT_CONN, "invalid srt streamid=%s", streamid.c_str());
    }

    // discovery vhost, resolve the vhost from config
    SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req_->vhost);
    if (parsed_vhost) {
        req_->vhost = parsed_vhost->arg0();
    }

    if (! _srs_config->get_srt_enabled(req_->vhost)) {
        return srs_error_new(ERROR_SRT_CONN, "srt disabled, vhost=%s", req_->vhost.c_str());
    }

    srs_trace("@srt, streamid=%s, stream_url=%s, vhost=%s, app=%s, stream=%s, param=%s",
              streamid.c_str(), req_->get_stream_url().c_str(), req_->vhost.c_str(), req_->app.c_str(), req_->stream.c_str(), req_->param.c_str());

    if ((err = _srs_srt_sources->fetch_or_create(req_, &srt_source_)) != srs_success) {
        return srs_error_wrap(err, "fetch srt source");
    }

    if ((err = http_hooks_on_connect()) != srs_success) {
        return srs_error_wrap(err, "on connect");
    }

    if (mode == SrtModePush) {
        err = publishing();      /推流处理
    } else if (mode == SrtModePull) {
        err = playing();        // 拉流处理
    }
    
    http_hooks_on_close();
    
    return err;
}

Guess you like

Origin blog.csdn.net/u012794472/article/details/126810729