类 

UDP:

ISrsUdpHandler 

SrsUdpListener

TCP:

ISrsTCPHandler 

SrsTCPListener

app/srs_app_listener.hpp

app/srs_app_listener.cpp

/**
 * the udp packet handler.
 */
class ISrsUdpHandler
{
public:
    ISrsUdpHandler();
    virtual ~ISrsUdpHandler();
public:
    /**
     * when fd changed, for instance, reload the listen port,
     * notify the handler and user can do something.
     */
    virtual int on_stfd_change(st_netfd_t fd);
public:
    /**
     * when udp listener got a udp packet, notice server to process it.
     * @param type, the client type, used to create concrete connection,
     *       for instance RTMP connection to serve client.
     * @param from, the udp packet from address.
     * @param buf, the udp packet bytes, user should copy if need to use.
     * @param nb_buf, the size of udp packet bytes.
     * @remark user should never use the buf, for it's a shared memory bytes.
     */
    virtual int on_udp_packet(sockaddr_in* from, char* buf, int nb_buf) = 0;
};
ISrsUdpHandler用来处理UDP数据包
SrsMpegtsOverUdp类
app/srs_app_mpegts_udp.h
app/srs_app_mpegts_udp.cpp
/**
 * the mpegts over udp stream caster.
 */
class SrsMpegtsOverUdp : virtual public ISrsTsHandler
, virtual public ISrsUdpHandler
{
private:
    SrsBuffer* stream;
    SrsTsContext* context;
    SrsSimpleStream* buffer;
    std::string output;
private:
    SrsSimpleRtmpClient* sdk;
private:
    SrsRawH264Stream* avc;
    std::string h264_sps;
    bool h264_sps_changed;
    std::string h264_pps;
    bool h264_pps_changed;
    bool h264_sps_pps_sent;
private:
    SrsRawAacStream* aac;
    std::string aac_specific_config;
private:
    SrsMpegtsQueue* queue;
    SrsPithyPrint* pprint;
public:
    SrsMpegtsOverUdp(SrsConfDirective* c);
    virtual ~SrsMpegtsOverUdp();
// interface ISrsUdpHandler
public:
    virtual int on_udp_packet(sockaddr_in* from, char* buf, int nb_buf);
private:
    virtual int on_udp_bytes(std::string host, int port, char* buf, int nb_buf);
// interface ISrsTsHandler
public:
    virtual int on_ts_message(SrsTsMessage* msg);
private:
    virtual int on_ts_video(SrsTsMessage* msg, SrsBuffer* avs);
    virtual int write_h264_sps_pps(uint32_t dts, uint32_t pts);
    virtual int write_h264_ipb_frame(char* frame, int frame_size, uint32_t dts, uint32_t pts);
    virtual int on_ts_audio(SrsTsMessage* msg, SrsBuffer* avs);
    virtual int write_audio_raw_frame(char* frame, int frame_size, SrsRawAacStreamCodec* codec, uint32_t dts);
private:
    virtual int rtmp_write_packet(char type, uint32_t timestamp, char* data, int size);
private:
    // Connect to RTMP server.
    virtual int connect();
    // Close the connection to RTMP server.
    virtual void close();
};

udp用作 mpegts over udp stream caster

/**
 * the tcp connection handler.
 */
class ISrsTcpHandler
{
public:
    ISrsTcpHandler();
    virtual ~ISrsTcpHandler();
public:
    /**
     * when got tcp client.
     */
    virtual int on_tcp_client(st_netfd_t stfd) = 0;
};
/**
 * bind and listen tcp port, use handler to process the client.
 */
class SrsTcpListener : public ISrsReusableThreadHandler
{
private:
    int _fd;
    st_netfd_t _stfd;
    SrsReusableThread* pthread;
private:
    ISrsTcpHandler* handler;
    std::string ip;
    int port;
public:
    SrsTcpListener(ISrsTcpHandler* h, std::string i, int p);
    virtual ~SrsTcpListener();
public:
    virtual int fd();
public:
    virtual int listen();
// interface ISrsReusableThreadHandler.
public:
    virtual int cycle();
};
int SrsUdpListener::listen()
{
    int ret = ERROR_SUCCESS;
    
    if ((_fd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
        ret = ERROR_SOCKET_CREATE;
        srs_error("create linux socket error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("create linux socket success. ip=%s, port=%d, fd=%d", ip.c_str(), port, _fd);
    
    int reuse_socket = 1;
    if (setsockopt(_fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) {
        ret = ERROR_SOCKET_SETREUSE;
        srs_error("setsockopt reuse-addr error. ip=%s, port=%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("setsockopt reuse-addr success. ip=%s, port=%d, fd=%d", ip.c_str(), port, _fd);
    
    sockaddr_in addr;
    addr.sin_family = AF_INET;
    addr.sin_port = htons(port);
    addr.sin_addr.s_addr = inet_addr(ip.c_str());
    if (bind(_fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) {
        ret = ERROR_SOCKET_BIND;
        srs_error("bind socket error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("bind socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
    
    if ((_stfd = st_netfd_open_socket(_fd)) == NULL){
        ret = ERROR_ST_OPEN_SOCKET;
        srs_error("st_netfd_open_socket open socket failed. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("st open socket success. ep=%s:%d, fd=%d", ip.c_str(), port, _fd);
    
    if ((ret = pthread->start()) != ERROR_SUCCESS) {
        srs_error("st_thread_create listen thread error. ep=%s:%d, ret=%d", ip.c_str(), port, ret);
        return ret;
    }
    srs_verbose("create st listen thread success, ep=%s:%d", ip.c_str(), port);
    
    return ret;
}
SrsUdpListener::listen函数
1. 创建udp套接字,绑定设置成员 _fd
2. call
_stfd = st_netfd_open_socket(_fd)
3. call pthread->start()启动coroutine,在 cycle函数里 call st_recvfrom接收数据,call handler->on_udp_packet处理数据

SrsTcpListener的listen函数和SrsUdpListener一样
SrsTcpListener的cycle函数
int SrsTcpListener::cycle()
{
    int ret = ERROR_SUCCESS;
    
    st_netfd_t client_stfd = st_accept(_stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT);
    
    if(client_stfd == NULL){
        // ignore error.
        if (errno != EINTR) {
            srs_error("ignore accept thread stoppped for accept client error");
        }
        return ret;
    }
    srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd));
    
    if ((ret = handler->on_tcp_client(client_stfd)) != ERROR_SUCCESS) {
        srs_warn("accept client error. ret=%d", ret);
        return ret;
    }
    
    return ret;
}

call  st_accept接收连接,call  handler->on_tcp_client处理连接

-------------------------------- 分割线 -------------------------------------------------------------------------------

文件

app/srs_app_server.h app/srs_app_server.cpp

// listener type for server to identify the connection,
// that is, use different type to process the connection.
enum SrsListenerType
{
    // RTMP client,
    SrsListenerRtmpStream = 0,
    // HTTP api,
    SrsListenerHttpApi = 1,
    // HTTP stream, HDS/HLS/DASH
    SrsListenerHttpStream = 2,
    // UDP stream, MPEG-TS over udp.
    SrsListenerMpegTsOverUdp = 3,
    // TCP stream, RTSP stream.
    SrsListenerRtsp = 4,
    // TCP stream, FLV stream over HTTP.
    SrsListenerFlv = 5,
};
/**
 * 通用  listener
 */
class SrsListener
{
protected:
    SrsListenerType type;
protected:
    std::string ip;
    int port;
    SrsServer* server;
public:
    SrsListener(SrsServer* svr, SrsListenerType t);
    virtual ~SrsListener();
public:
    virtual SrsListenerType listen_type();
    virtual int listen(std::string i, int p) = 0;
};
/**
 * tcp listener.
* SrsBufferListener类在一下位置被调用
* 1.   SrsBufferListener 用于 SrsListenerRtmpStream 类型,用于处理RTMP连接
*    在SrsServer::listen_rtmp函数里构造
*
* 2. SrsBufferListener 用于SrsListenerHttpApi类型,用于处理http api的连接
* 在 SrsServer::listen_http_api函数里构造
*
* 3. SrsBufferListener 用于 SrsListenerHttpStream类型,用于处理 http stream连接
* 用于 http flv,http mp4,hls等
* 在 SrsServer::listen_http_stream函数里构造
*/ class SrsBufferListener : virtual public SrsListener, virtual public ISrsTcpHandler { private: SrsTcpListener* listener; public: SrsBufferListener(SrsServer* server, SrsListenerType type); virtual ~SrsBufferListener(); public: virtual int listen(std::string ip, int port); // ISrsTcpHandler public: virtual int on_tcp_client(st_netfd_t stfd); };
int SrsBufferListener::listen(string i, int p)
{
    int ret = ERROR_SUCCESS;
    
    ip = i;
    port = p;
    
    srs_freep(listener);
    listener = new SrsTcpListener(this, ip, port);
    
    if ((ret = listener->listen()) != ERROR_SUCCESS) {
        srs_error("tcp listen failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("listen thread current_cid=%d, "
             "listen at port=%d, type=%d, fd=%d started success, ep=%s:%d",
             _srs_context->get_id(), p, type, listener->fd(), i.c_str(), p);
    
    srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd());
    
    return ret;
}

int SrsBufferListener::on_tcp_client(st_netfd_t stfd)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = server->accept_client(type, stfd)) != ERROR_SUCCESS) {
        srs_warn("accept client error. ret=%d", ret);
        return ret;
    }
    
    return ret;
}

接收的客户端连接都通过SrsServer的accept_client函数处理,

int SrsServer::accept_client(SrsListenerType type, st_netfd_t stfd)
{
    int ret = ERROR_SUCCESS;
    
    SrsConnection* conn = fd2conn(type, stfd);
    if (conn == NULL) {
        srs_close_stfd(stfd);
        return ERROR_SUCCESS;
    }
    srs_assert(conn);
    
    // directly enqueue, the cycle thread will remove the client.
    conns.push_back(conn);
    srs_verbose("add conn to vector.");
    
    // cycle will start process thread and when finished remove the client.
    // @remark never use the conn, for it maybe destroyed.
    if ((ret = conn->start()) != ERROR_SUCCESS) {
        return ret;
    }
    srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret);
    
    return ret;
}

1. 调用 fd2conn函数得到  SrsConnection对象,存到

std::vector<SrsConnection*> conns;
2. call  conn->start(),启动coroutine,在单独的协程里处理一个特定客户的请求
SrsConnection* SrsServer::fd2conn(SrsListenerType type, st_netfd_t stfd)
{
    int ret = ERROR_SUCCESS;
    
    int fd = st_netfd_fileno(stfd);
    string ip = srs_get_peer_ip(fd);
    
    // for some keep alive application, for example, the keepalived,
    // will send some tcp packet which we cann't got the ip,
    // we just ignore it.
    if (ip.empty()) {
        srs_info("ignore empty ip client, fd=%d.", fd);
        return NULL;
    }
    
    // check connection limitation.
    int max_connections = _srs_config->get_max_connections();
    if (handler && (ret = handler->on_accept_client(max_connections, (int)conns.size()) != ERROR_SUCCESS)) {
        srs_error("handle accept client failed, drop client: clients=%d, max=%d, fd=%d. ret=%d", (int)conns.size(), max_connections, fd, ret);
        return NULL;
    }
    if ((int)conns.size() >= max_connections) {
        srs_error("exceed the max connections, drop client: clients=%d, max=%d, fd=%d", (int)conns.size(), max_connections, fd);
        return NULL;
    }
    
    // avoid fd leak when fork.
    // @see https://github.com/ossrs/srs/issues/518
    if (true) {
        int val;
        if ((val = fcntl(fd, F_GETFD, 0)) < 0) {
            ret = ERROR_SYSTEM_PID_GET_FILE_INFO;
            srs_error("fnctl F_GETFD error! fd=%d. ret=%#x", fd, ret);
            return NULL;
        }
        val |= FD_CLOEXEC;
        if (fcntl(fd, F_SETFD, val) < 0) {
            ret = ERROR_SYSTEM_PID_SET_FILE_INFO;
            srs_error("fcntl F_SETFD error! fd=%d ret=%#x", fd, ret);
            return NULL;
        }
    }
    
    SrsConnection* conn = NULL;
    
    if (type == SrsListenerRtmpStream) {
        conn = new SrsRtmpConn(this, stfd, ip);
    } else if (type == SrsListenerHttpApi) {
        conn = new SrsHttpApi(this, stfd, http_api_mux, ip);
    } else if (type == SrsListenerHttpStream) {
        conn = 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 NULL;
    }
    
    return conn;
}

1.  type == SrsListenerRtmpStream ==> new SrsRtmpConn(this, stfd, ip);

2.  type == SrsListenerHttpApi       ==> new SrsHttpApi(this, stfd, http_api_mux, ip);
3.  type == SrsListenerHttpStream ==> new SrsResponseOnlyHttpConn(this, stfd, http_server, ip);
class SrsRtmpConn : public virtual SrsConnection, public virtual ISrsReloadHandler
    SrsRtmpConn(SrsServer* svr, st_netfd_t c, std::string cip);
class SrsHttpApi : virtual public SrsConnection, virtual public ISrsReloadHandler
    SrsHttpApi(IConnectionManager* cm, st_netfd_t fd, SrsHttpServeMux* m, std::string cip);
/**
 * drop body of request, only process the response.
 */
class SrsResponseOnlyHttpConn : public SrsHttpConn
{
public:
    SrsResponseOnlyHttpConn(IConnectionManager* cm, st_netfd_t fd, ISrsHttpServeMux* m, std::string cip);
    virtual ~SrsResponseOnlyHttpConn();
public:
    // Directly read a HTTP request message.
    // It's exported for HTTP stream, such as HTTP FLV, only need to write to client when
    // serving it, but we need to start a thread to read message to detect whether FD is closed.
    // @see https://github.com/ossrs/srs/issues/636#issuecomment-298208427
    // @remark Should only used in HTTP-FLV streaming connection.
    virtual int pop_message(ISrsHttpMessage** preq);
public:
    virtual int on_got_http_message(ISrsHttpMessage* msg);
};
/**
 * The http connection which request the static or stream content.
 */
class SrsHttpConn : public SrsConnection
/**
 * the basic connection of SRS,
 * all connections accept from listener must extends from this base class,
 * server will add the connection to manager, and delete it when remove.
 */
class SrsConnection : virtual public ISrsConnection, virtual public ISrsOneCycleThreadHandler
    , virtual public IKbpsDelta, virtual public ISrsReloadHandler
{
private:
    /**
     * each connection start a green thread,
     * when thread stop, the connection will be delete by server.
     */
    SrsOneCycleThread* pthread;
    /**
     * the id of connection.
     */
    int id;

可见生成的是SrsConnection的子类,下一篇分析SrsConnection类及其子类

/**
 * the tcp listener, for rtsp server.
 */
class SrsRtspListener : virtual public SrsListener, virtual public ISrsTcpHandler
{
private:
    SrsTcpListener* listener;
    ISrsTcpHandler* caster;
public:
    SrsRtspListener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c);
    virtual ~SrsRtspListener();
public:
    virtual int listen(std::string i, int p);
    // ISrsTcpHandler
public:
    virtual int on_tcp_client(st_netfd_t stfd);
};
SrsRtspListener 接收 rtsp推流,由caster将rtsp stream变换成rtmp stream后推送到 rtmp server
SrsRtspListener::SrsRtspListener(SrsServer* svr, SrsListenerType t, SrsConfDirective* c) : SrsListener(svr, t)
{
    listener = NULL;
    
    // the caller already ensure the type is ok,
    // we just assert here for unknown stream caster.
    srs_assert(type == SrsListenerRtsp);
    if (type == SrsListenerRtsp) {
        caster = new SrsRtspCaster(c);
    }
}

SrsRtspListener::~SrsRtspListener()
{
    srs_freep(caster);
    srs_freep(listener);
}

int SrsRtspListener::listen(string i, int p)
{
    int ret = ERROR_SUCCESS;
    
    // the caller already ensure the type is ok,
    // we just assert here for unknown stream caster.
    srs_assert(type == SrsListenerRtsp);
    
    ip = i;
    port = p;
    
    srs_freep(listener);
    listener = new SrsTcpListener(this, ip, port);
    
    if ((ret = listener->listen()) != ERROR_SUCCESS) {
        srs_error("rtsp caster listen failed. ret=%d", ret);
        return ret;
    }
    srs_info("listen thread listen at port=%d, type=%d, fd=%d started success, ep=%s:%d", port, type, listener->fd(), ip.c_str(), port);
    
    srs_trace("%s listen at tcp://%s:%d, fd=%d", srs_listener_type2string(type).c_str(), ip.c_str(), port, listener->fd());
    
    return ret;
}

int SrsRtspListener::on_tcp_client(st_netfd_t stfd)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = caster->on_tcp_client(stfd)) != ERROR_SUCCESS) {
        srs_warn("accept client error. ret=%d", ret);
        return ret;
    }
    
    return ret;
}
SrsRtspCaster

文件app/srs_app_rtsp.hpp

app/srs_app_rtsp.cpp

/**
 * the caster for rtsp.
 */
class SrsRtspCaster : public ISrsTcpHandler
{
private:
    std::string output;
    int local_port_min;
    int local_port_max;
    // key: port, value: whether used.
    std::map<int, bool> used_ports;
private:
    std::vector<SrsRtspConn*> clients;
public:
    SrsRtspCaster(SrsConfDirective* c);
    virtual ~SrsRtspCaster();
public:
    /**
     * alloc a rtp port from local ports pool.
     * @param pport output the rtp port.
     */
    virtual int alloc_port(int* pport);
    /**
     * free the alloced rtp port.
     */
    virtual void free_port(int lpmin, int lpmax);
// interface ISrsTcpHandler
public:
    virtual int on_tcp_client(st_netfd_t stfd);
    // internal methods.
public:
    virtual void remove(SrsRtspConn* conn);
};
int SrsRtspCaster::on_tcp_client(st_netfd_t stfd)
{
    int ret = ERROR_SUCCESS;
    // output 是要输出的目标 RTMP推流地址
    SrsRtspConn* conn = new SrsRtspConn(this, stfd, output);
    
    if ((ret = conn->serve()) != ERROR_SUCCESS) {
        srs_error("rtsp: serve client failed. ret=%d", ret);
        srs_freep(conn);
        return ret;
    }
    
    clients.push_back(conn);
    srs_info("rtsp: start thread to serve client.");
    
    return ret;
}
class SrsRtspConn : public ISrsOneCycleThreadHandler
int SrsRtspConn::serve()
{
    int ret = ERROR_SUCCESS;

  
if ((ret = skt->initialize(stfd)) != ERROR_SUCCESS) {
return ret;
    }
    
    return trd->start();
}
成员变量
SrsStSocket* skt;
int SrsRtspConn::do_cycle()
{
    int ret = ERROR_SUCCESS;
    
    // retrieve ip of client.
    std::string ip = srs_get_peer_ip(st_netfd_fileno(stfd));
    srs_trace("rtsp: serve %s", ip.c_str());
    
    // consume all rtsp messages.
    for (;;) {
        SrsRtspRequest* req = NULL;
// rtsp类型 SrsRtspStack,构造参数skt
if ((ret = rtsp->recv_message(&req)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("rtsp: recv request failed. ret=%d", ret); } return ret; } SrsAutoFree(SrsRtspRequest, req); srs_info("rtsp: got rtsp request"); if (req->is_options()) { SrsRtspOptionsResponse* res = new SrsRtspOptionsResponse((int)req->seq); res->session = session; if ((ret = rtsp->send_message(res)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("rtsp: send OPTIONS response failed. ret=%d", ret); } return ret; } } else if (req->is_announce()) { if (rtsp_tcUrl.empty()) { rtsp_tcUrl = req->uri; } size_t pos = string::npos; if ((pos = rtsp_tcUrl.rfind(".sdp")) != string::npos) { rtsp_tcUrl = rtsp_tcUrl.substr(0, pos); } srs_parse_rtmp_url(rtsp_tcUrl, rtsp_tcUrl, rtsp_stream); srs_assert(req->sdp); video_id = ::atoi(req->sdp->video_stream_id.c_str()); audio_id = ::atoi(req->sdp->audio_stream_id.c_str()); video_codec = req->sdp->video_codec; audio_codec = req->sdp->audio_codec; audio_sample_rate = ::atoi(req->sdp->audio_sample_rate.c_str()); audio_channel = ::atoi(req->sdp->audio_channel.c_str()); h264_sps = req->sdp->video_sps; h264_pps = req->sdp->video_pps; aac_specific_config = req->sdp->audio_sh; srs_trace("rtsp: video(#%d, %s, %s/%s), audio(#%d, %s, %s/%s, %dHZ %dchannels), %s/%s", video_id, video_codec.c_str(), req->sdp->video_protocol.c_str(), req->sdp->video_transport_format.c_str(), audio_id, audio_codec.c_str(), req->sdp->audio_protocol.c_str(), req->sdp->audio_transport_format.c_str(), audio_sample_rate, audio_channel, rtsp_tcUrl.c_str(), rtsp_stream.c_str() ); SrsRtspResponse* res = new SrsRtspResponse((int)req->seq); res->session = session; if ((ret = rtsp->send_message(res)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("rtsp: send ANNOUNCE response failed. ret=%d", ret); } return ret; } } else if (req->is_setup()) { srs_assert(req->transport); int lpm = 0; if ((ret = caster->alloc_port(&lpm)) != ERROR_SUCCESS) { srs_error("rtsp: alloc port failed. ret=%d", ret); return ret; } SrsRtpConn* rtp = NULL; if (req->stream_id == video_id) { srs_freep(video_rtp); rtp = video_rtp = new SrsRtpConn(this, lpm, video_id); } else { srs_freep(audio_rtp); rtp = audio_rtp = new SrsRtpConn(this, lpm, audio_id); } if ((ret = rtp->listen()) != ERROR_SUCCESS) { srs_error("rtsp: rtp listen at port=%d failed. ret=%d", lpm, ret); return ret; } srs_trace("rtsp: #%d %s over %s/%s/%s %s client-port=%d-%d, server-port=%d-%d", req->stream_id, (req->stream_id == video_id)? "Video":"Audio", req->transport->transport.c_str(), req->transport->profile.c_str(), req->transport->lower_transport.c_str(), req->transport->cast_type.c_str(), req->transport->client_port_min, req->transport->client_port_max, lpm, lpm + 1 ); // create session. if (session.empty()) { session = "O9EaZ4bf"; // TODO: FIXME: generate session id. } SrsRtspSetupResponse* res = new SrsRtspSetupResponse((int)req->seq); res->client_port_min = req->transport->client_port_min; res->client_port_max = req->transport->client_port_max; res->local_port_min = lpm; res->local_port_max = lpm + 1; res->session = session; if ((ret = rtsp->send_message(res)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("rtsp: send SETUP response failed. ret=%d", ret); } return ret; } } else if (req->is_record()) { SrsRtspResponse* res = new SrsRtspResponse((int)req->seq); res->session = session; if ((ret = rtsp->send_message(res)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("rtsp: send SETUP response failed. ret=%d", ret); } return ret; } } } return ret; }