SrsConnection* conn =NULL;if (type ==SrsListenerRtmpStream) {
conn= new SrsRtmpConn(this, client_stfd);
}else if (type ==SrsListenerHttpApi) {
#ifdef SRS_AUTO_HTTP_API
conn= new SrsHttpApi(this, client_stfd, http_api_mux);#elsesrs_warn("close http client for server not support http-api");
srs_close_stfd(client_stfd);returnret;#endif}else if (type ==SrsListenerHttpStream) {
#ifdef SRS_AUTO_HTTP_SERVER
conn= new SrsResponseOnlyHttpConn(this, client_stfd, http_server);#elsesrs_warn("close http client for server not support http-server");
srs_close_stfd(client_stfd);returnret;#endif}else{//TODO: FIXME: handler others
}
srs_assert(conn);
创建完成后调用 conn->start()启动,此后这个client的请求都在这个st thread 里完成
SrsConection类
/**
* the manager for connection.
* SrsServer类实现了这一接口,用来对SrsConnection类进行管理.*/
classIConnectionManager
{public:
IConnectionManager();virtual ~IConnectionManager();public:/**
* remove the specified connection.*/
virtual void remove(SrsConnection* c) = 0;
};/**
* 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 : public virtual ISrsOneCycleThreadHandler, public virtualIKbpsDelta
{private:/**
* each connection start a green thread,
* when thread stop, the connection will be delete by server.
* 业务处理线程*/SrsOneCycleThread*pthread;/**
* the id of connection.*/
intid;protected:/**
* the manager object to manage the connection.*/IConnectionManager*manager;/**
* the underlayer st fd handler.*/st_netfd_t stfd;/**
* the ip of client.*/std::stringip;/**
* whether the connection is disposed,
* when disposed, connection should stop cycle and cleanup itself.*/
booldisposed;/**
* whether connection is expired, application definition.
* when expired, the connection must never be served and quit ASAP.*/
boolexpired;public:
SrsConnection(IConnectionManager*cm, st_netfd_t c);virtual ~SrsConnection();public:/**
* to dipose the connection.*/
virtual voiddispose();/**
* start the client green thread.
* when server get a client from listener,
* 1. server will create an concrete connection(for instance, RTMP connection),
* 2. then add connection to its connection manager,
* 3. start the client thread by invoke this start()
* when client cycle thread stop, invoke the on_thread_stop(), which will use server
* to remove the client by server->remove(this).*/
virtual intstart();//interface ISrsOneCycleThreadHandler
public:/**
* the thread cycle function,
* when serve connection completed, terminate the loop which will terminate the thread,
* thread will invoke the on_thread_stop() when it terminated.*/
virtual intcycle();/**
* when the thread cycle finished, thread will invoke the on_thread_stop(),
* which will remove self from server, server will remove the connection from manager
* then delete the connection.*/
virtual voidon_thread_stop();public:/**
* get the srs id which identify the client.*/
virtual intsrs_id();/**
* set connection to expired.*/
virtual voidexpire();protected:/**
* for concrete connection to do the cycle.
* 具体connection 的处理函数,参见SrsRtmpConn.*/
virtual int do_cycle() = 0;
};
intSrsConnection::start()
{
// 启动SrsOneCycleThread类return pthread->start();
}intSrsConnection::cycle()
{int ret =ERROR_SUCCESS;
//这是个全局的id生成器,也用来生成st thread的id
_srs_context->generate_id();
id= _srs_context->get_id();
// 取得客户端的ip地址
ip=srs_get_peer_ip(st_netfd_fileno(stfd));
// 调用子类如SrsRtmpConn的处理函数
ret=do_cycle();//if socket io error, set to closed.
if(srs_is_client_gracefully_close(ret)) {
ret=ERROR_SOCKET_CLOSED;
}//success.
if (ret ==ERROR_SUCCESS) {
srs_trace("client finished.");
}//client close peer.
if (ret ==ERROR_SOCKET_CLOSED) {
srs_warn("client disconnect peer. ret=%d", ret);
}returnERROR_SUCCESS;
}voidSrsConnection::on_thread_stop()
{//TODO: FIXME: never remove itself, use isolate thread to do cleanup.
// 用在SrsServer类的conns向量数组中删除此对象 manager->remove(this);
}intSrsConnection::srs_id()
{returnid;
}voidSrsConnection::expire()
{
expired= true;
}
SrsRtmpConn类
SrsRtmpConn::SrsRtmpConn(SrsServer*svr, st_netfd_t c)
: SrsConnection(svr, c)
{
server=svr;
req= newSrsRequest();
res= newSrsResponse();
skt= newSrsStSocket(c);//实现ISrsProtocolReaderWriter接口,即st socket的读写函数,以及读写超时,发送和接受到的字节数
rtmp= newSrsRtmpServer(skt);// 提供rtmp-command-protocol服务,是一个上层的面向协议,media stream的服务,
// 例如连接vhost/app,播放流,获取视频/音频数据,与之对应的是 SrsRtmpClient.
refer= newSrsRefer();
bandwidth= newSrsBandwidth();
security= newSrsSecurity();
duration= 0;
kbps= newSrsKbps();
kbps->set_io(skt, skt);
wakable=NULL;
mw_sleep=SRS_PERF_MW_SLEEP;
mw_enabled= false;
realtime=SRS_PERF_MIN_LATENCY_ENABLED;
send_min_interval= 0;
tcp_nodelay= false;
client_type=SrsRtmpConnUnknown;
// 在配置改变时,支持reload
_srs_config->subscribe(this);
}
构造函数初始化成员变量
//TODO: return detail message when error for client.
intSrsRtmpConn::do_cycle()
{int ret =ERROR_SUCCESS;
srs_trace("RTMP client ip=%s", ip.c_str());
// 设置SrsRtmpServer里面的底层数据通讯类SrsProtocol的读写超时
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);
// 握手if ((ret = rtmp->handshake()) !=ERROR_SUCCESS) {
srs_error("rtmp handshake failed. ret=%d", ret);returnret;
}
srs_verbose("rtmp handshake success");
// 获取连接的appif ((ret = rtmp->connect_app(req)) !=ERROR_SUCCESS) {
srs_error("rtmp connect vhost/app failed. ret=%d", ret);returnret;
}
srs_verbose("rtmp connect app success");//set client ip to request.
req->ip =ip;
// 假设推流地址 rtmp://192.168.151.151/live/marstv//discovery vhost, resolve the vhost from config
req->vhost 是192.168.151.151
SrsConfDirective* parsed_vhost = _srs_config->get_vhost(req->vhost);
// 没有设置 192.168.151.151 这样的vhost parsed_vhost是默认的vhostif(parsed_vhost) {
req->vhost = parsed_vhost->arg0();// req->vhost 是 __defaultVhost__
}
srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s",
req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str());if (req->schema.empty() || req->vhost.empty() || req->port.empty() || req->app.empty()) {
ret=ERROR_RTMP_REQ_TCURL;
srs_error("discovery tcUrl failed."
"tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d",
req->tcUrl.c_str(), req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), req->app.c_str(), ret);returnret;
}//check vhost// 在 check_vhost里面会调用 http_hooks_on_connect
if ((ret = check_vhost()) !=ERROR_SUCCESS) {
srs_error("check vhost failed. ret=%d", ret);returnret;
}
srs_verbose("check vhost success.");
srs_trace("connect app,"
"tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, args=%s",
req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(),
req->schema.c_str(), req->vhost.c_str(), req->port.c_str(),
req->app.c_str(), (req->args? "(obj)":"null"));//show client identity
if(req->args) {
std::stringsrs_version;
std::stringsrs_server_ip;int srs_pid = 0;int srs_id = 0;
SrsAmf0Any* prop =NULL;if ((prop = req->args->ensure_property_string("srs_version")) !=NULL) {
srs_version= prop->to_str();
}if ((prop = req->args->ensure_property_string("srs_server_ip")) !=NULL) {
srs_server_ip= prop->to_str();
}if ((prop = req->args->ensure_property_number("srs_pid")) !=NULL) {
srs_pid= (int)prop->to_number();
}if ((prop = req->args->ensure_property_number("srs_id")) !=NULL) {
srs_id= (int)prop->to_number();
}
srs_info("edge-srs ip=%s, version=%s, pid=%d, id=%d",
srs_server_ip.c_str(), srs_version.c_str(), srs_pid, srs_id);if (srs_pid > 0) {
srs_trace("edge-srs ip=%s, version=%s, pid=%d, id=%d",
srs_server_ip.c_str(), srs_version.c_str(), srs_pid, srs_id);
}
}
ret=service_cycle();
http_hooks_on_close();returnret;
}
SrsRtmpConn::do_cycle最后调用 service_cycle函数,
service_cycle有一个循环调用 stream_service_recycle处理 client_type为 SrsRtmpConnPlay(调用playing函数),
SrsRtmpConnFMLEPublish,SrsRtmpConnHaivisionPublish,SrsRtmpConnFlashPublish,
intSrsRtmpConn::service_cycle()
{int ret =ERROR_SUCCESS;
//发送一个SrsSetWindowAckSizePacket类型的数据包if ((ret = rtmp->set_window_ack_size((int)(2.5 * 1000 * 1000))) !=ERROR_SUCCESS) {
srs_error("set window acknowledgement size failed. ret=%d", ret);returnret;
}
srs_verbose("set window acknowledgement size success");
//
发送一个SrsSetWindowAckSizePacket类型的数据包
if ((ret = rtmp->set_peer_bandwidth((int)(2.5 * 1000 * 1000), 2)) !=ERROR_SUCCESS) {
srs_error("set peer bandwidth failed. ret=%d", ret);returnret;
}
srs_verbose("set peer bandwidth success");//get the ip which client connected.
std::string local_ip =srs_get_local_ip(st_netfd_fileno(stfd));
// 检测虚拟主机的bandcheck指令//do bandwidth test if connect to the vhost which is for bandwidth check.
if (_srs_config->get_bw_check_enabled(req->vhost)) {return bandwidth->bandwidth_check(rtmp, skt, req, local_ip);
}//do token traverse before serve it.//@seehttps://github.com/ossrs/srs/pull/239
if (true) {bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost);bool edge_traverse = _srs_config->get_vhost_edge_token_traverse(req->vhost);if (vhost_is_edge &&edge_traverse) {if ((ret = check_edge_token_traverse_auth()) !=ERROR_SUCCESS) {
srs_warn("token auth failed, ret=%d", ret);returnret;
}
}
}//set chunk size to larger.//set the chunk size before any larger response greater than 128,//to make OBS happy, @seehttps://github.com/ossrs/srs/issues/454
int chunk_size = _srs_config->get_chunk_size(req->vhost);if ((ret = rtmp->set_chunk_size(chunk_size)) !=ERROR_SUCCESS) {
srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret);returnret;
}
//发送一个SrsSetChunkSizePacket类型的数据包
srs_info("set chunk_size=%d success", chunk_size);
//发送一个SrsConnectAppResPacket类型的数据包//response the client connect ok.
if ((ret = rtmp->response_connect_app(req, local_ip.c_str())) !=ERROR_SUCCESS) {
srs_error("response connect app failed. ret=%d", ret);returnret;
}
srs_verbose("response connect app success");
//发送一个SrsOnBWDonePacket类型的数据包if ((ret = rtmp->on_bw_done()) !=ERROR_SUCCESS) {
srs_error("on_bw_done failed. ret=%d", ret);returnret;
}
srs_verbose("on_bw_done success");while (!disposed) {
ret=stream_service_cycle();//stream service must terminated with error, never success.//when terminated with success, it's user required to stop.
if (ret ==ERROR_SUCCESS) {continue;
}//when not system control error, fatal error, return.
if (!srs_is_system_control_error(ret)) {if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
srs_error("stream service cycle failed. ret=%d", ret);
}returnret;
}//for republish, continue service
if (ret ==ERROR_CONTROL_REPUBLISH) {//set timeout to a larger value, wait for encoder to republish.
rtmp->set_send_timeout(SRS_REPUBLISH_RECV_TIMEOUT_US);
rtmp->set_recv_timeout(SRS_REPUBLISH_SEND_TIMEOUT_US);
srs_trace("control message(unpublish) accept, retry stream service.");continue;
}//for "some" system control error,//logical accept and retry stream service.
if (ret ==ERROR_CONTROL_RTMP_CLOSE) {//TODO: FIXME: use ping message to anti-death of socket.//@see:https://github.com/ossrs/srs/issues/39
//set timeout to a larger value, for user paused.
rtmp->set_recv_timeout(SRS_PAUSED_RECV_TIMEOUT_US);
rtmp->set_send_timeout(SRS_PAUSED_SEND_TIMEOUT_US);
srs_trace("control message(close) accept, retry stream service.");continue;
}//for other system control message, fatal error.
srs_error("control message(%d) reject as error. ret=%d", ret, ret);returnret;
}returnret;
}
intSrsRtmpConn::stream_service_cycle()
{int ret =ERROR_SUCCESS;
SrsRtmpConnType type;
//接受client的消息,是play 或 publishif ((ret = rtmp->identify_client(res->stream_id, type, req->stream, req->duration)) !=ERROR_SUCCESS) {if (!srs_is_client_gracefully_close(ret)) {
srs_error("identify client failed. ret=%d", ret);
}returnret;
}
req->strip();
srs_trace("client identified, type=%s, stream_name=%s, duration=%.2f",
srs_client_type_string(type).c_str(), req->stream.c_str(), req->duration);//security check
//根据ip地址对client进行过滤,位于srs_app_security.cpp:39//先检查allow规则,在检查deny规则,deny规则的优先级较高
if ((ret = security->check(type, ip, req)) !=ERROR_SUCCESS) {
srs_error("security check failed. ret=%d", ret);returnret;
}
srs_info("security check ok");//Never allow the empty stream name, for HLS may write to a file with empty name.//@seehttps://github.com/ossrs/srs/issues/834
if (req->stream.empty()) {
ret=ERROR_RTMP_STREAM_NAME_EMPTY;
srs_error("RTMP: Empty stream name not allowed, ret=%d", ret);returnret;
}//client is identified, set the timeout to service timeout.
rtmp->set_recv_timeout(SRS_CONSTS_RTMP_RECV_TIMEOUT_US);
rtmp->set_send_timeout(SRS_CONSTS_RTMP_SEND_TIMEOUT_US);//find a source to serve.
SrsSource* source =NULL;if ((ret = SrsSource::fetch_or_create(req, server, &source)) !=ERROR_SUCCESS) {returnret;
}
srs_assert(source!=NULL);//update the statistic when source disconveried.
SrsStatistic* stat =SrsStatistic::instance();if ((ret = stat->on_client(_srs_context->get_id(), req, this, type)) !=ERROR_SUCCESS) {
srs_error("stat client failed. ret=%d", ret);returnret;
}bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost);bool enabled_cache = _srs_config->get_gop_cache(req->vhost);
srs_trace("source url=%s, ip=%s, cache=%d, is_edge=%d, source_id=%d[%d]",
req->get_stream_url().c_str(), ip.c_str(), enabled_cache, vhost_is_edge,
source->source_id(), source->source_id());
source->set_cache(enabled_cache);
client_type=type;switch(type) {caseSrsRtmpConnPlay: {
srs_verbose("start to play stream %s.", req->stream.c_str());//response connection start play
if ((ret = rtmp->start_play(res->stream_id)) !=ERROR_SUCCESS) {
srs_error("start to play stream failed. ret=%d", ret);returnret;
}if ((ret = http_hooks_on_play()) !=ERROR_SUCCESS) {
srs_error("http hook on_play failed. ret=%d", ret);returnret;
}
srs_info("start to play stream %s success", req->stream.c_str());
ret=playing(source);
http_hooks_on_stop();returnret;
}caseSrsRtmpConnFMLEPublish: {
srs_verbose("FMLE start to publish stream %s.", req->stream.c_str());
//rtmp->start_fmle_publish功能
//1. 接收SrsFMLEStartPacket类型的数据包
// 将其中的transaction_id保存下来
// 2. 将transaction_id 作为参数构造SrsFMLEStartResPacket包返回给客户端
// 3. 接收SrsCreateStreamPacket类型的数据包
// 保存其中的transaction_id
//4. 用接收的transaction_id和服务端的stream_id初始化
// SrsCreateStreamResPacket包返回个publish client
// 5.接收SrsPublishPacket
//6. 回复SrsOnStatusCallPacket 参数
// command_name --> onFCPublish
// StatusCode --> NetStream.Publish.Start
// StatusDescription --> "Started publishing stream."
//7. 在回复一个SrsOnStatusCallPacket参数
// StatusLevel --> StatusLevelStatus
// StatusCode --> StatusCodePublishStart
// StatusDescription --> 同上
// StatusClientId --> RTMP_SIG_CLIENT_ID --> "ASAICiss"if ((ret = rtmp->start_fmle_publish(res->stream_id)) !=ERROR_SUCCESS) {
srs_error("start to publish stream failed. ret=%d", ret);returnret;
}returnpublishing(source);
}caseSrsRtmpConnHaivisionPublish: {
srs_verbose("Haivision start to publish stream %s.", req->stream.c_str());if ((ret = rtmp->start_haivision_publish(res->stream_id)) !=ERROR_SUCCESS) {
srs_error("start to publish stream failed. ret=%d", ret);returnret;
}returnpublishing(source);
}caseSrsRtmpConnFlashPublish: {
srs_verbose("flash start to publish stream %s.", req->stream.c_str());if ((ret = rtmp->start_flash_publish(res->stream_id)) !=ERROR_SUCCESS) {
srs_error("flash start to publish stream failed. ret=%d", ret);returnret;
}returnpublishing(source);
}default: {
ret=ERROR_SYSTEM_CLIENT_INVALID;
srs_info("invalid client type=%d. ret=%d", type, ret);returnret;
}
}returnret;
}
最后调用publishing函数,代码如下
int SrsRtmpConn::publishing(SrsSource*source)
{int ret =ERROR_SUCCESS;if ((ret = refer->check(req->pageUrl, _srs_config->get_refer_publish(req->vhost))) !=ERROR_SUCCESS) {
srs_error("check publish_refer failed. ret=%d", ret);returnret;
}
srs_verbose("check publish_refer success.");if ((ret = http_hooks_on_publish()) !=ERROR_SUCCESS) {
srs_error("http hook on_publish failed. ret=%d", ret);returnret;
}bool vhost_is_edge = _srs_config->get_vhost_is_edge(req->vhost);if ((ret = acquire_publish(source, vhost_is_edge)) ==ERROR_SUCCESS) {//use isolate thread to recv,//@see:https://github.com/ossrs/srs/issues/237
SrsPublishRecvThread trd(rtmp, req,
st_netfd_fileno(stfd),0, this, source,
client_type!=SrsRtmpConnFlashPublish,
vhost_is_edge);
srs_info("start to publish stream %s success", req->stream.c_str());
ret= do_publishing(source, &trd);//stop isolate recv thread
trd.stop();
}//whatever the acquire publish, always release publish.//when the acquire error in the midlle-way, the publish state changed,//but failed, so we must cleanup it.//@seehttps://github.com/ossrs/srs/issues/474
//@remark when stream is busy, should never release it.
if (ret !=ERROR_SYSTEM_STREAM_BUSY) {
release_publish(source, vhost_is_edge);
}
http_hooks_on_unpublish();returnret;
}
主要功能如下:
1. 调用SrsRefer的check函数
2. 调用 http_hooks_on_publish函数
3. 调用 acquire_publish函数
4. 如果3 成功,新建SrsPublishRecvThread实例后,执行do_publishing函数
4. call http_hooks_on_unpublish函数
int SrsRtmpConn::do_publishing(SrsSource* source, SrsPublishRecvThread*trd)
{int ret =ERROR_SUCCESS;
SrsPithyPrint* pprint =SrsPithyPrint::create_rtmp_publish();
SrsAutoFree(SrsPithyPrint, pprint);//start isolate recv thread.
if ((ret = trd->start()) !=ERROR_SUCCESS) {
srs_error("start isolate recv thread failed. ret=%d", ret);returnret;
}//change the isolate recv thread context id,//merge its log to current thread.
int receive_thread_cid = trd->get_cid();
trd->set_cid(_srs_context->get_id());//initialize the publish timeout.
publish_1stpkt_timeout = _srs_config->get_publish_1stpkt_timeout(req->vhost);
publish_normal_timeout= _srs_config->get_publish_normal_timeout(req->vhost);//set the sock options.
set_sock_options();if (true) {bool mr = _srs_config->get_mr_enabled(req->vhost);int mr_sleep = _srs_config->get_mr_sleep_ms(req->vhost);
srs_trace("start publish mr=%d/%d, p1stpt=%d, pnt=%d, tcp_nodelay=%d, rtcid=%d",
mr, mr_sleep, publish_1stpkt_timeout, publish_normal_timeout, tcp_nodelay, receive_thread_cid);
}
int64_t nb_msgs= 0;while (!disposed) {
pprint->elapse();//when source is set to expired, disconnect it.
if(expired) {
ret=ERROR_USER_DISCONNECT;
srs_error("connection expired. ret=%d", ret);returnret;
}//cond wait for timeout.
if (nb_msgs == 0) {//when not got msgs, wait for a larger timeout.//@seehttps://github.com/ossrs/srs/issues/441
trd->wait(publish_1stpkt_timeout);
}else{
trd->wait(publish_normal_timeout);
}//check the thread error code.
if ((ret = trd->error_code()) !=ERROR_SUCCESS) {if (!srs_is_system_control_error(ret) && !srs_is_client_gracefully_close(ret)) {
srs_error("recv thread failed. ret=%d", ret);
}returnret;
}//when not got any messages, timeout.
if (trd->nb_msgs() <=nb_msgs) {
ret=ERROR_SOCKET_TIMEOUT;
srs_warn("publish timeout %dms, nb_msgs=%"PRId64", ret=%d",
nb_msgs?publish_normal_timeout : publish_1stpkt_timeout, nb_msgs, ret);break;
}
nb_msgs= trd->nb_msgs();//reportable
if (pprint->can_print()) {
kbps->sample();bool mr = _srs_config->get_mr_enabled(req->vhost);int mr_sleep = _srs_config->get_mr_sleep_ms(req->vhost);
srs_trace("age(),
kbps->get_send_kbps(), kbps->get_send_kbps_30s(), kbps->get_send_kbps_5m(),
kbps->get_recv_kbps(), kbps->get_recv_kbps_30s(), kbps->get_recv_kbps_5m(),
mr, mr_sleep, publish_1stpkt_timeout, publish_normal_timeout
);
}
}returnret;
}
等待SrsPublishRecvThread结束
接受包的处理流程
SrsRecvThread::cycle()调用SrsRtmpServer的recv_message函数接收类型为SrsCommonMessage的消息
call handler->handle(msg)
也就是SrsPublishRecvThread的handle方法
在转到SrsRtmpConn的handle_publish_msg,检查消息头部,对命令的格式进行检查
在转到SrsRtmpConn的process_publish_msg
int SrsRtmpConn::process_publish_message(SrsSource* source, SrsCommonMessage* msg, boolvhost_is_edge)
{int ret =ERROR_SUCCESS;//for edge, directly proxy message to origin.
if(vhost_is_edge) {if ((ret = source->on_edge_proxy_publish(msg)) !=ERROR_SUCCESS) {
srs_error("edge publish proxy msg failed. ret=%d", ret);returnret;
}returnret;
}//process audio packet
if (msg->header.is_audio()) {if ((ret = source->on_audio(msg)) !=ERROR_SUCCESS) {
srs_error("source process audio message failed. ret=%d", ret);returnret;
}returnret;
}//process video packet
if (msg->header.is_video()) {if ((ret = source->on_video(msg)) !=ERROR_SUCCESS) {
srs_error("source process video message failed. ret=%d", ret);returnret;
}returnret;
}//process aggregate packet
if (msg->header.is_aggregate()) {if ((ret = source->on_aggregate(msg)) !=ERROR_SUCCESS) {
srs_error("source process aggregate message failed. ret=%d", ret);returnret;
}returnret;
}//process onMetaData
if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
SrsPacket* pkt =NULL;if ((ret = rtmp->decode_message(msg, &pkt)) !=ERROR_SUCCESS) {
srs_error("decode onMetaData message failed. ret=%d", ret);returnret;
}
SrsAutoFree(SrsPacket, pkt);if (dynamic_cast(pkt)) {
SrsOnMetaDataPacket* metadata = dynamic_cast(pkt);if ((ret = source->on_meta_data(msg, metadata)) !=ERROR_SUCCESS) {
srs_error("source process onMetaData message failed. ret=%d", ret);returnret;
}
srs_info("process onMetaData message success.");returnret;
}
srs_info("ignore AMF0/AMF3 data message.");returnret;
}returnret;
}
根据vhost 是否是 edge
数据是 audio video aggregate 调用 SrsSource类的
on_edge_proxy_publish
on_audio
on_video
on_aggregate
on_metadata