Este artigo é baseado na análise do srs 5.0:
1. Inicie o ouvinte e crie um objeto SrsMpegtsSrtConn para cada fluxo push-pull srt:
O serviço srt é iniciado na função: run_hybrid_server; crie o serviço SrsSrtServerAdapter e execute a função SrsSrtServerAdapter::run.
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: Crie um objeto SrsSrtServer e 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() chama listen_srt_mpegts; listen_srt_mpegts cria um objeto SrsSrtAcceptor e executa 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;
}
Função SrsSrtAcceptor::listen: cria o objeto SrsSrtListener e transfere o objeto da classe SrsSrtAcceptor, executa: função SrsSrtListener::listen.
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;
}
Função SrsSrtListener::listen: Cria um objeto SrsSTCoroutine e executa 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 : Cria uma co-rotina e executa SrsFastCoroutine::cycle ; onde a função executa 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;
}
Construtor de classe SrsFastCoroutine: o manipulador aponta para SrsSrtListener, o manipulador->ciclo na verdade chama SrsSrtListener::ciclo. A função SrsSrtListener::ciclo chama o manipulador_->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_ aponta para SrsSrtAcceptor, handler_->on_srt_client realmente chama SrsSrtAcceptor::on_srt_client,
Chamadas de função SrsSrtAcceptor::on_srt_client 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:
A principal função da função SrsSrtServer::accept_srt_client é criar SrsMpegtsSrtConn para cada fluxo push-pull srt e executar SrsMpegtsSrtConn::start. Até agora, a função do serviço de monitoramento srt está concluída.
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. Processamento do serviço de streaming SrsMpegtsSrtConn:
Passe SrsSTCoroutine, a corrotina passa no objeto SrsMpegtsSrtConn e a função de execução da corrotina final é SrsMpegtsSrtConn::cycle(); A função SrsMpegtsSrtConn::cycle() chama a função do_cycle: do_cycle
A função do_cycle é diferenciada de acordo com o tipo de objeto SrsMpegtsSrtConn: push stream executa a publicação; pull stream executa o processamento de reprodução.
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;
}