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;
}