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);