srs使用了state-threads协程库,是单线程多协程模型。不用考虑线程安全,数据不用加锁。
int SrsServer::listen()中启动if ((ret = listen_rtmp()) != ERROR_SUCCESS)
进一步启动:SrsListener* listener = new SrsBufferListener(this, SrsListenerRtmpStream);
然后启动:
SrsTcpListener::SrsTcpListener(ISrsTcpHandler* h, string i, int p)
{
handler = h;
ip = i;
port = p;
lfd = NULL;
trd = new SrsDummyCoroutine();
}
在协程的cycle中,会调用到if ((ret = handler->on_tcp_client(client_stfd)) != ERROR_SUCCESS)
在on_tcp_client()中,会调用if ((ret = server->accept_client(type, stfd)) != ERROR_SUCCESS)
int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd)
中调用fd2conn(SrsListenerType type, srs_netfd_t stfd, SrsConnection** pconn)
这是建立rtmp/tcp连接最为重要的func。根据连接类型,生成相应的实例来处理。
if (type == SrsListenerRtmpStream) {
*pconn = new SrsRtmpConn(this, stfd, ip);
} else if (type == SrsListenerHttpApi) {
*pconn = new SrsHttpApi(this, stfd, http_api_mux, ip);
} else if (type == SrsListenerHttpStream) {
*pconn = new SrsResponseOnlyHttpConn(this, stfd, http_server, ip);
} else {
srs_warn("close for no service handler. fd=%d, ip=%s", fd, ip.c_str());
srs_close_stfd(stfd);
return err;
}
协程将调度单位减小到函数,上下文切换不需要内核参与,开销降到最低。
没有锁竞争,没有信号处理。保留了程序对请求的线性处理逻辑,提高了程序的开发效率,可扩展性和可维护性。
协程库state threads library(以下简称st)是一个基于setjmp/longjmp实现的C语言版用户线程库或协程库(user level thread)。
SrsRtmpConn会继承SrsConnection,生成一个srs协程SrsSTCoroutine。
SrsConnection::SrsConnection(IConnectionManager* cm, srs_netfd_t c, string cip)
{
manager = cm;
stfd = c;
ip = cip;
create_time = srsu2ms(srs_get_system_time());
skt = new SrsStSocket();
clk = new SrsWallClock();
kbps = new SrsKbps(clk);
kbps->set_io(skt, skt);
trd = new SrsSTCoroutine("conn", this);
}
协程int SrsConnection::cycle()调用了do_cycle(),派生类实现了这个方法。
在这儿正式进入rtmp协议处理阶段。先进行握手:rtmp->handshake()等操作,然后进入service_cycle();。
srs_error_t SrsRtmpConn::stream_service_cycle()
{
先进行tmp->identify_client客户端身份识别。
然后根据根据客户端类型(type)进入不同分支。
SrsRtmpConnPlay是客户端播流。
SrsRtmpConnFMLEPublish 是Rtmp推流到服务器。
}
对于发布流(推流到srs)来说,看SrsRtmpConnFMLEPublish。
对于拉流(从SRS拉流)来说,看SrsRtmpConnPlay。
case SrsRtmpConnFMLEPublish: {
if ((err = rtmp->start_fmle_publish(info->res->stream_id)) != srs_success) {
return srs_error_wrap(err, "rtmp: start FMLE publish");
}
return publishing(source);
}
主处理函数:publishing(source);
if ((err = http_hooks_on_publish()) != srs_success) { //这里可以是推流鉴权hook
return srs_error_wrap(err, "rtmp: callback on publish");
}
协程处理:
srs_error_t SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread* rtrd)
srs_error_t SrsPublishRecvThread::consume(SrsCommonMessage* msg)
这个consume()函数是rtmp协议处理主体。
录像dvr是SRS的重要功能,也一样用协程来完成的。
class SrsDvrFlvSegmenter是处理flv格式录像的主体类。
class SrsDvrMp4Segmenter是处理mp4格式录像的主体类。