概述
在前面的博文,我们介绍了nginx-rtmp模块的初始化、用户请求的接收和请求分发等,现在我们使用一个例子来串联起来整个用户请求处理的逻辑;
根据rtmp协议的要求,在完成三次握手之后,客户都端会使用amf格式数据发送”connect“请求到服务端,我们看一下在服务端是怎么处理的,根据前一篇博文中的介绍,当请求到达ngx_rtmp_receive_message方法之后,根据ngx_rtmp_header_t *h中的type字段,这个时候type应该是针对于amf解析方式,所以请求会到达ngx_rtmp_amf_message_handler中,在ngx_rtmp_amf_message_handler方法中,获取到命令为”connect“,根据在ngx_rtmp_cmd_postconfiguration方法中的注册的方法,我们可以知道,”connect“命令对应的方法为ngx_rtmp_cmd_connect_init;我们看一下ngx_rtmp_cmd_connect_init方法中做了什么事情:
static ngx_int_t ngx_rtmp_cmd_connect_init(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in)
{
size_t len;
static ngx_rtmp_connect_t v;
static ngx_rtmp_amf_elt_t in_cmd[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("app"),
v.app, sizeof(v.app) },
{ NGX_RTMP_AMF_STRING,
ngx_string("flashVer"),
v.flashver, sizeof(v.flashver) },
{ NGX_RTMP_AMF_STRING,
ngx_string("swfUrl"),
v.swf_url, sizeof(v.swf_url) },
{ NGX_RTMP_AMF_STRING,
ngx_string("tcUrl"),
v.tc_url, sizeof(v.tc_url) },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audioCodecs"),
&v.acodecs, sizeof(v.acodecs) },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videoCodecs"),
&v.vcodecs, sizeof(v.vcodecs) },
{ NGX_RTMP_AMF_STRING,
ngx_string("pageUrl"),
v.page_url, sizeof(v.page_url) },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("objectEncoding"),
&v.object_encoding, 0},
};
static ngx_rtmp_amf_elt_t in_elts[] = {
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&v.trans, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
in_cmd, sizeof(in_cmd) },
};
//获取请求包中的参数
ngx_memzero(&v, sizeof(v));
if (ngx_rtmp_receive_amf(s, in, in_elts, sizeof(in_elts) / sizeof(in_elts[0])))
{
return NGX_ERROR;
}
len = ngx_strlen(v.app);
if (len > 10 && !ngx_memcmp(v.app + len - 10, "/_definst_", 10)) {
v.app[len - 10] = 0;
} else if (len && v.app[len - 1] == '/') {
v.app[len - 1] = 0;
}
//参数填充
ngx_rtmp_cmd_fill_args(v.app, v.args);
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"connect: app='%s' args='%s' flashver='%s' swf_url='%s' "
"tc_url='%s' page_url='%s' acodecs=%uD vcodecs=%uD "
"object_encoding=%ui",
v.app, v.args, v.flashver, v.swf_url, v.tc_url, v.page_url,
(uint32_t)v.acodecs, (uint32_t)v.vcodecs,
(ngx_int_t)v.object_encoding);
//调用connect,这个是一个函数指针,赋值是在ngx_rtmp_cmd_postconfiguration函数中进行的
return ngx_rtmp_connect(s, &v);
}
根据ngx_rtmp_cmd_postconfiguration中给ngx_rtmp_connect的赋值,我们看了一知道,ngx_rtmp_connect实际指向的是ngx_rtmp_cmd_connect方法,所欲i我们看一下该方法做的事情
static ngx_int_t
ngx_rtmp_cmd_connect(ngx_rtmp_session_t *s, ngx_rtmp_connect_t *v)
{
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_core_app_conf_t **cacfp;
ngx_uint_t n;
ngx_rtmp_header_t h;
u_char *p;
static double trans;
static double capabilities = NGX_RTMP_CAPABILITIES;
static double object_encoding = 0;
static ngx_rtmp_amf_elt_t out_obj[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("fmsVer"),
NGX_RTMP_FMS_VERSION, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("capabilities"),
&capabilities, 0 },
};
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
"status", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("code"),
"NetConnection.Connect.Success", 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("description"),
"Connection succeeded.", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("objectEncoding"),
&object_encoding, 0 }
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"_result", 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_null_string,
&trans, 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_obj, sizeof(out_obj) },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf, sizeof(out_inf) },
};
if (s->connected) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"connect: duplicate connection");
return NGX_ERROR;
}
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
//获取到connect传上来的参数
trans = v->trans;
/* fill session parameters */
s->connected = 1;
ngx_memzero(&h, sizeof(h));
h.csid = NGX_RTMP_CSID_AMF_INI;
h.type = NGX_RTMP_MSG_AMF_CMD;
#define NGX_RTMP_SET_STRPAR(name) \
s->name.len = ngx_strlen(v->name); \
s->name.data = ngx_palloc(s->connection->pool, s->name.len); \
ngx_memcpy(s->name.data, v->name, s->name.len)
//将connect的参数拷贝到用户的session中去
NGX_RTMP_SET_STRPAR(app);
NGX_RTMP_SET_STRPAR(args);
NGX_RTMP_SET_STRPAR(flashver);
NGX_RTMP_SET_STRPAR(swf_url);
NGX_RTMP_SET_STRPAR(tc_url);
NGX_RTMP_SET_STRPAR(page_url);
#undef NGX_RTMP_SET_STRPAR
p = ngx_strlchr(s->app.data, s->app.data + s->app.len, '?');
if (p) {
s->app.len = (p - s->app.data);
}
//将音视频编解码方式存储到用户session中去
s->acodecs = (uint32_t) v->acodecs;
s->vcodecs = (uint32_t) v->vcodecs;
/* find application & set app_conf */
//查找到用户请求对应的application
cacfp = cscf->applications.elts;
for(n = 0; n < cscf->applications.nelts; ++n, ++cacfp)
{
if ((*cacfp)->name.len == s->app.len
&& ngx_strncmp((*cacfp)->name.data, s->app.data, s->app.len) == 0)
{
/* found app! */
s->app_conf = (*cacfp)->app_conf;
break;
}
}
if (s->app_conf == NULL)
{
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"connect: application not found: '%V'", &s->app);
return NGX_ERROR;
}
object_encoding = v->object_encoding;
//想客户端发送chunk_size,bandwidth等字段以及响应用户请求
return ngx_rtmp_send_ack_size(s, cscf->ack_window) != NGX_OK ||
ngx_rtmp_send_bandwidth(s, cscf->ack_window,
NGX_RTMP_LIMIT_DYNAMIC) != NGX_OK ||
ngx_rtmp_send_chunk_size(s, cscf->chunk_size) != NGX_OK ||
ngx_rtmp_send_amf(s, &h, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]))
!= NGX_OK ? NGX_ERROR : NGX_OK;
}
好了,上面是整个用户请求处理流程,如果针对于其他的用户请求,大家可以跟着源码中的流程逐步的分析,这个地方知识给一个参考的例子
总结
到目前位置,我们将nginx-rtmp的整个core模块进行了介绍;到目前位置,整个core模块主要做的事情的事情为“初始化各个子模块”、“完成网络事件”、“解析rtmp协议”以及完成事件分发。
接下来,将会按照各个子模块,进行分析几个子模块,作为例子