ngx_rtmp_notify_module 简介及配置
ngx_rtmp_notify_module的主要功能是上报RTMP Session的状态。在直播系统中非常重要,我们可以通过notify事件上报,来实时监控直播流状态。并且还可以根据on_play和on_publish的重定向功能,做直播服务器集群内部的直播流调度功能。
on_connect
设置连接回调HTTP的请求。当接收到客户端connect指令,会创建一个同步的HTTP请求,在请求返回前,进程被挂起。如果HTTP返回2xx,会继续RTMP Session。当返回值是3xx时,RTMP会重定向到另一个application,HTTP返回Header中的Location内容,即为新的application名称。否则,当前连接会被丢弃。
on_disconnect
设置连接断开的回调HTTP请求。
onplay
设置播放回调HTTP的请求。当接收到客户端play指令,会创建一个同步的HTTP请求,在请求返回前,进程被挂起。HTTP返回值分析:
- code=2xx:继续RTMP Session的next_play处理。
- code=3xx:RTMP根据HTTP请求返回的Header中Location内容重定向到另一个流。如果Location值以rtmp://为开头,会创建一个远程中继连接。中继rtmp必须是IP地址,而不能是域名,并在只在nginx版本1.3.10以上才有效工作。
- 丢弃RTMP连接
on_publish
和上面的on_play类似,唯一的不同是设置在publish指令回调。并且对中继是推流,而不是拉流。
on_done
设置播放/发布终止回调。 以上所有都适用于此。 但是,未为该回调检查HTTP状态代码。
on_play_done
与on_done行为相同,但仅适用于播放结束事件。
on_publish_done
与on_done行为相同,但仅适用于推流结束事件。
on_record_done
设置record_done回调。 除常见的HTTP回调变量外,它还接收以下值:
recorder:配置中的录制名称或默认录制。
path:录制文件路径。
on_update
在notify_update_timeout事件时调用此回调。 如果请求返回的HTTP结果不是2xx连接,则终止。 这可用于同步过期的会话。 另外两个参数time和timestamp传递给此处理程序:
time:播放或推流时间(秒)。
timestamp:发送到客户端最后一个音视频包的RTMP时间戳。
notify_update_timeout
设置on_update调用的间隔时间,默认30秒。
notify_update_strict
on_updata回调的严格模式。默认关闭。打开后,所有的连接错误,例如HTTP超时或者空响应,会被当作失败处理,导致连接断开。当关闭时,只有HTTP响应非2xx时,才会触发失败处理。
notify_relay_redirect
为on_play和on_publish远程重定向启用本地流重定向。 新的流名称是用于远程重定向的RTMP URL的MD5哈希。 默认为关闭。
notify_method
设置通知的HTTP方法。 默认为POST,其内容类型为application / x-www-form-urlencoded。 在某些情况下,最好使用GET,例如,如果您计划在nginx的http {}部分中处理呼叫。 在这种情况下,您可以使用arg_ *变量访问参数。
ngx_rtmp_notify_module 主要处理函数源码分析
预处理
static ngx_int_t
ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf)
{
next_connect = ngx_rtmp_connect;
ngx_rtmp_connect = ngx_rtmp_notify_connect;
next_disconnect = ngx_rtmp_disconnect;
ngx_rtmp_disconnect = ngx_rtmp_notify_disconnect;
next_publish = ngx_rtmp_publish;
ngx_rtmp_publish = ngx_rtmp_notify_publish;
next_play = ngx_rtmp_play;
ngx_rtmp_play = ngx_rtmp_notify_play;
next_close_stream = ngx_rtmp_close_stream;
ngx_rtmp_close_stream = ngx_rtmp_notify_close_stream;
next_record_done = ngx_rtmp_record_done;
ngx_rtmp_record_done = ngx_rtmp_notify_record_done;
return NGX_OK;
}
postconfiguration阶段,将对应的事件处理函数添加到RTMP到处理函数队列中。
事件 | 函数 | 说明 |
ngx_rtmp_connect | ngx_rtmp_notify_connect | 对应on_connect配置 |
ngx_rtmp_disconnect | ngx_rtmp_notify_disconnect | 对应断开连接on_disconnect的配置 |
ngx_rtmp_publish | ngx_rtmp_notify_publish | 推流开始事件回调 |
ngx_rtmp_play | ngx_rtmp_notify_play | 拉流开始事件回调 |
ngx_rtmp_close_stream | ngx_rtmp_notify_close_stream | 关闭流事件回调 |
ngx_rtmp_record_done | ngx_rtmp_notify_record_done | 录制结束事件回调 |
推流开始事件回调
1、获取publish上报的url地址:
url = nacf->url[NGX_RTMP_NOTIFY_PUBLISH];
2、初始化publish notify的基本参数:
ngx_rtmp_notify_init(s, v->name, v->args, NGX_RTMP_NOTIFY_PUBLISHING);
3、真正的开始http访问,创建ngx_rtmp_netcall_create_pt,和对于http返回结果的处理函数的handle。
ngx_memzero(&ci, sizeof(ci));
ci.url = url;
ci.create = ngx_rtmp_notify_publish_create;
ci.handle = ngx_rtmp_notify_publish_handle;
ci.arg = v;
ci.argsize = sizeof(*v);
return ngx_rtmp_netcall_create(s, &ci);
4、ngx_rtmp_notify_publish_create:主要用于创建http request时,添加参数。
publish参数 | 说明 |
call | 表明是publish调用 |
name | 流名称 |
type | AMF类型 |
args | 直接附加上推流时携带的参数 |
b = ngx_create_temp_buf(pool,
sizeof("&call=publish") +
sizeof("&name=") + name_len * 3 +
sizeof("&type=") + type_len * 3 +
1 + args_len);
if (b == NULL) {
return NULL;
}
pl->buf = b;
pl->next = NULL;
b->last = ngx_cpymem(b->last, (u_char*) "&call=publish",
sizeof("&call=publish") - 1);
b->last = ngx_cpymem(b->last, (u_char*) "&name=", sizeof("&name=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, v->name, name_len,
NGX_ESCAPE_ARGS);
b->last = ngx_cpymem(b->last, (u_char*) "&type=", sizeof("&type=") - 1);
b->last = (u_char*) ngx_escape_uri(b->last, v->type, type_len,
NGX_ESCAPE_ARGS);
if (args_len) {
*b->last++ = '&';
b->last = (u_char *) ngx_cpymem(b->last, v->args, args_len);
}
5、ngx_rtmp_notify_publish_handle 对于publish http返回结果的处理:
rc = ngx_rtmp_notify_parse_http_retcode(s, in);
if (rc == NGX_ERROR) {
ngx_rtmp_notify_clear_flag(s, NGX_RTMP_NOTIFY_PUBLISHING);
return NGX_ERROR;
}
首先是判断http返回值,并进行相应的处理
retcode | 处理结果 |
2** | 进行下一步,next_play |
3** | 建立rtmp relay |
其他 | 返回NGX_ERROR |
下面我们分析介绍retcode=3**的具体处理逻辑:
rc = ngx_rtmp_notify_parse_http_header(s, in, &location, name,
sizeof(name) - 1);
if (rc <= 0) {
goto next;
}
if (ngx_strncasecmp(name, (u_char *) "rtmp://", 7)) {
*ngx_cpymem(v->name, name, rc) = 0;
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"notify: publish redirect to '%s'", v->name);
goto next;
}
/* push */
nacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_notify_module);
if (nacf->relay_redirect) {
ngx_rtmp_notify_set_name(v->name, NGX_RTMP_MAX_NAME, name, (size_t) rc);
}
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"notify: push '%s' to '%*s'", v->name, rc, name);
local_name.data = v->name;
local_name.len = ngx_strlen(v->name);
ngx_memzero(&target, sizeof(target));
u = &target.url;
u->url = local_name;
u->url.data = name + 7;
u->url.len = rc - 7;
u->default_port = 1935;
u->uri_part = 1;
u->no_resolve = 1; /* want ip here */
if (ngx_parse_url(s->connection->pool, u) != NGX_OK) {
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"notify: push failed '%V'", &local_name);
return NGX_ERROR;
}
ngx_rtmp_relay_push(s, &local_name, &target);
首先是获取http header中location的值,因为要将location作为下一步relay push的目的地址,所以location必须是rtmp://的合法地址。创建target.url,并赋值解析。最后是调用ngx_rtmp_relay_module中的ngx_rtmp_relay_push函数创建relay的直播流。
拉流开始事件回调
拉流开始事件回调,同上一节中介绍的推流事件回调基本类似。
ngx_rtmp_notify_update 定时通知机制
ngx_rtmp_notify_update是在ngx_rtmp_notify_init调用时配置的。需要首先判断配置文件中是否配置了update项,然后在判断是否已经设置了update事件,最后是创建update timer,并将它添加到定时器中。
if (nacf->url[NGX_RTMP_NOTIFY_UPDATE] == NULL ||
nacf->update_timeout == 0)
{
return;
}
if (ctx->update_evt.timer_set) {
return;
}
ctx->start = ngx_cached_time->sec;
e = &ctx->update_evt;
e->data = s->connection;
e->log = s->connection->log;
e->handler = ngx_rtmp_notify_update;
ngx_add_timer(e, nacf->update_timeout);
ngx_rtmp_notify_update函数解析:
和ngx_rtmp_notify_publish类似,创建http的ngx_rtmp_notify_update_create,和调用的返回处理函数ngx_rtmp_notify_update_handle。
ci.url = url;
ci.create = ngx_rtmp_notify_update_create;
ci.handle = ngx_rtmp_notify_update_handle;
if (ngx_rtmp_netcall_create(s, &ci) == NGX_OK) {
return;
}
/* schedule next update on connection error */
ngx_rtmp_notify_update_handle(s, NULL, NULL);
ngx_rtmp_notify_update_create 阶段,添加http参数,主要是上报流名称、时间戳等参数,对于publish和play的不同点如下:
if (ctx->flags & NGX_RTMP_NOTIFY_PUBLISHING) {
ngx_str_set(&sfx, "_publish");
} else if (ctx->flags & NGX_RTMP_NOTIFY_PLAYING) {
ngx_str_set(&sfx, "_play");
} else {
ngx_str_null(&sfx);
}
ngx_rtmp_notify_update_handle阶段:主要是将update timer再重新添加进定时器里面。但是对于update_strict模式,只有返回值是非2**,就会返回NGX_ERROR。
rc = ngx_rtmp_notify_parse_http_retcode(s, in);
if ((!nacf->update_strict && rc == NGX_ERROR) ||
(nacf->update_strict && rc != NGX_OK))
{
ngx_log_error(NGX_LOG_INFO, s->connection->log, 0,
"notify: update failed");
return NGX_ERROR;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_notify_module);
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"notify: schedule update %Mms",
nacf->update_timeout);
ngx_add_timer(&ctx->update_evt, nacf->update_timeout);