diff --git a/ngx_rtmp_amf.c b/ngx_rtmp_amf.c index f133d0e..e91ebfb 100644 --- a/ngx_rtmp_amf.c +++ b/ngx_rtmp_amf.c @@ -27,7 +27,7 @@ ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len) return dst; } -#define NGX_RTMP_AMF_DEBUG_SIZE 16 +#define NGX_RTMP_AMF_DEBUG_SIZE 72 #ifdef NGX_DEBUG static void diff --git a/ngx_rtmp_notify_module.c b/ngx_rtmp_notify_module.c index 25975d6..f17b01c 100644 --- a/ngx_rtmp_notify_module.c +++ b/ngx_rtmp_notify_module.c @@ -28,6 +28,8 @@ static char *ngx_rtmp_notify_on_app_event(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_rtmp_notify_method(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_rtmp_notify_send_redirect(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_int_t ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_notify_create_app_conf(ngx_conf_t *cf); static char * ngx_rtmp_notify_merge_app_conf(ngx_conf_t *cf, @@ -70,16 +72,17 @@ typedef struct { ngx_url_t *url[NGX_RTMP_NOTIFY_APP_MAX]; ngx_flag_t active; ngx_uint_t method; + ngx_flag_t send_redirect; ngx_msec_t update_timeout; ngx_flag_t update_strict; ngx_flag_t relay_redirect; - ngx_flag_t send_redirect; } ngx_rtmp_notify_app_conf_t; typedef struct { ngx_url_t *url[NGX_RTMP_NOTIFY_SRV_MAX]; ngx_uint_t method; + ngx_flag_t send_redirect; } ngx_rtmp_notify_srv_conf_t; @@ -194,9 +197,9 @@ static ngx_command_t ngx_rtmp_notify_commands[] = { { ngx_string("notify_send_redirect"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, - ngx_conf_set_flag_slot, + ngx_rtmp_notify_send_redirect, NGX_RTMP_APP_CONF_OFFSET, - offsetof(ngx_rtmp_notify_app_conf_t, send_redirect), + 0, NULL }, ngx_null_command @@ -247,10 +250,10 @@ ngx_rtmp_notify_create_app_conf(ngx_conf_t *cf) } nacf->method = NGX_CONF_UNSET_UINT; + nacf->send_redirect = NGX_CONF_UNSET; nacf->update_timeout = NGX_CONF_UNSET_MSEC; nacf->update_strict = NGX_CONF_UNSET; nacf->relay_redirect = NGX_CONF_UNSET; - nacf->send_redirect = NGX_CONF_UNSET; return nacf; } @@ -276,11 +279,11 @@ ngx_rtmp_notify_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->method, prev->method, NGX_RTMP_NETCALL_HTTP_POST); + ngx_conf_merge_value(conf->send_redirect, prev->send_redirect, 0); ngx_conf_merge_msec_value(conf->update_timeout, prev->update_timeout, 30000); ngx_conf_merge_value(conf->update_strict, prev->update_strict, 0); ngx_conf_merge_value(conf->relay_redirect, prev->relay_redirect, 0); - ngx_conf_merge_value(conf->send_redirect, prev->send_redirect, 0); return NGX_CONF_OK; } @@ -302,6 +305,7 @@ ngx_rtmp_notify_create_srv_conf(ngx_conf_t *cf) } nscf->method = NGX_CONF_UNSET_UINT; + nscf->send_redirect = NGX_CONF_UNSET; return nscf; } @@ -320,6 +324,7 @@ ngx_rtmp_notify_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->method, prev->method, NGX_RTMP_NETCALL_HTTP_POST); + ngx_conf_merge_value(conf->send_redirect, prev->send_redirect, 0); return NGX_CONF_OK; } @@ -967,7 +972,9 @@ ngx_rtmp_notify_connect_handle(ngx_rtmp_session_t *s, void *arg, ngx_chain_t *in) { ngx_rtmp_connect_t *v = arg; - ngx_int_t rc; + ngx_int_t rc, send; + ngx_str_t local_name; + ngx_rtmp_notify_srv_conf_t *nscf; u_char app[NGX_RTMP_MAX_NAME]; static ngx_str_t location = ngx_string("location"); @@ -977,19 +984,81 @@ ngx_rtmp_notify_connect_handle(ngx_rtmp_session_t *s, return NGX_ERROR; } - if (rc == NGX_AGAIN) { - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "notify: connect redirect received"); - - rc = ngx_rtmp_notify_parse_http_header(s, in, &location, app, - sizeof(app) - 1); - if (rc > 0) { - *ngx_cpymem(v->app, app, rc) = 0; - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "notify: connect redirect to '%s'", v->app); - } + if (rc != NGX_AGAIN) { + goto next; } + /* HTTP 3xx */ + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: connect redirect received"); + + rc = ngx_rtmp_notify_parse_http_header(s, in, &location, app, + sizeof(app) - 1); + if (rc <= 0) { + goto next; + } + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: parsed location '%*s'", rc, app); + + /* switch app */ + + if (ngx_strncasecmp(app, (u_char *) "rtmp://", 7)) { + *ngx_cpymem(v->app, app, rc) = 0; + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: connect redirect to '%s'", v->app); + goto next; + } + + /* redirect */ + + nscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_notify_module); + + if (nscf->send_redirect) { + // Send 302 redirect and go next + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: connect send 302 redirect"); + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: -- for app '%s' to new location '%*s'", v->app, rc, app); + + local_name.data = ngx_palloc(s->connection->pool, rc+1); + local_name.len = rc; + *ngx_cpymem(local_name.data, app, rc) = 0; + + /* MAGICK HERE */ + + if (!ngx_strncasecmp(s->flashver.data, (u_char *) "FMLE/", 5)) { + // Official method, by FMS SDK + send = ngx_rtmp_send_redirect_status(s, "onStatus", "Connect here", local_name); + send &= ngx_rtmp_send_redirect_status(s, "netStatus", "Connect here", local_name); + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: connect send(o) status = '%ui'", send == NGX_OK); + } else { + // Something by rtmpdump lib + send = ngx_rtmp_send_redirect_status(s, "_error", "Connect here", local_name); + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: connect send(e) status = '%ui'", send == NGX_OK); + } + + ngx_pfree(s->connection->pool, local_name.data); + + // Something by rtmpdump lib + send = ngx_rtmp_send_close_method(s, "close"); + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: connect send(e) close method = '%ui'", send == NGX_OK); + + return send; +// return next_disconnect(s); +// Don't close connection here! Client must catch message and do it by itself. +// goto next; + } + +next: + return next_connect(s, v); } @@ -1015,7 +1084,7 @@ ngx_rtmp_notify_publish_handle(ngx_rtmp_session_t *s, void *arg, ngx_chain_t *in) { ngx_rtmp_publish_t *v = arg; - ngx_int_t rc; + ngx_int_t rc, send; ngx_str_t local_name; ngx_rtmp_relay_target_t target; ngx_url_t *u; @@ -1036,7 +1105,7 @@ ngx_rtmp_notify_publish_handle(ngx_rtmp_session_t *s, /* HTTP 3xx */ - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "notify: publish redirect received"); rc = ngx_rtmp_notify_parse_http_header(s, in, &location, name, @@ -1060,23 +1129,46 @@ ngx_rtmp_notify_publish_handle(ngx_rtmp_session_t *s, // Send 302 redirect and go next ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "notify: send 302 redirect for stream '%s' to new location '%*s'", v->name, rc, name); + "notify: publish send 302 redirect"); + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: -- for stream '%s' to new location '%*s'", v->name, rc, name); - local_name.data = ngx_palloc(s->connection->pool, rc); + local_name.data = ngx_palloc(s->connection->pool, rc+1); local_name.len = rc; *ngx_cpymem(local_name.data, name, rc) = 0; - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "notify: check redirect to location: '%s'", local_name.data); + /* MAGICK HERE */ - ngx_rtmp_send_redirect_status(s, "Publish here", local_name); + if (!ngx_strncasecmp(s->flashver.data, (u_char *) "FMLE/", 5)) { + // Official method, by FMS SDK + send = ngx_rtmp_send_redirect_status(s, "onStatus", "Connect here", local_name); + send &= ngx_rtmp_send_redirect_status(s, "netStatus", "Connect here", local_name); - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "notify: release location memory"); + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: publish send(o) status = '%ui'", send == NGX_OK); + } else { + + // Something by rtmpdump lib + send = ngx_rtmp_send_redirect_status(s, "_error", "Connect here", local_name); + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: publish send(e) status = '%ui'", send == NGX_OK); + } ngx_pfree(s->connection->pool, local_name.data); - goto next; + ngx_rtmp_notify_clear_flag(s, NGX_RTMP_NOTIFY_PUBLISHING); +// return send; + + // Something by rtmpdump lib + send = ngx_rtmp_send_close_method(s); + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "notify: publish send(e) close method = '%ui'", send == NGX_OK); + + return send; +// return next_disconnect(s); +// Don't close connection here! Client must catch message and do it by itself. +// goto next; } else if (nacf->relay_redirect) { // Relay local streams, change name @@ -1737,6 +1829,37 @@ ngx_rtmp_notify_method(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } +static char * +ngx_rtmp_notify_send_redirect(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_rtmp_notify_app_conf_t *nacf = conf; + + ngx_rtmp_notify_srv_conf_t *nscf; + ngx_str_t *value; + + value = cf->args->elts; + value++; + + if (value->len == sizeof("on") - 1 && + ngx_strncasecmp(value->data, (u_char *) "on", value->len) == 0) + { + nacf->send_redirect = 1; + + } else if (value->len == sizeof("off") - 1 && + ngx_strncasecmp(value->data, (u_char *) "off", value->len) == 0) + { + nacf->send_redirect = 0; + + } else { + return "got unexpected send_redirect value"; + } + + nscf = ngx_rtmp_conf_get_module_srv_conf(cf, ngx_rtmp_notify_module); + nscf->send_redirect = nacf->send_redirect; + + return NGX_CONF_OK; +} + static ngx_int_t ngx_rtmp_notify_postconfiguration(ngx_conf_t *cf) diff --git a/ngx_rtmp_send.c b/ngx_rtmp_send.c index f5011fb..d2e2c4b 100644 --- a/ngx_rtmp_send.c +++ b/ngx_rtmp_send.c @@ -491,7 +491,7 @@ ngx_rtmp_create_status(ngx_rtmp_session_t *s, char *code, char* level, NULL, 0 }, { NGX_RTMP_AMF_OBJECT, - ngx_string("info"), + ngx_null_string, out_inf, sizeof(out_inf) }, }; @@ -503,6 +503,7 @@ ngx_rtmp_create_status(ngx_rtmp_session_t *s, char *code, char* level, out_inf[0].data = level; out_inf[1].data = code; out_inf[2].data = desc; + trans = 0; memset(&h, 0, sizeof(h)); @@ -597,18 +598,18 @@ ngx_rtmp_send_play_status(ngx_rtmp_session_t *s, char *code, char* level, // ----------- Based on Adobe FMS 3 application.redirectConnection description --------- // ngx_chain_t * -ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *desc, ngx_str_t to_url) +ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *callMethod, char *desc, ngx_str_t to_url) { ngx_rtmp_header_t h; - static double trans; + static double dtrans; static double dcode; ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "create redirect status: got data"); ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "create redirect status: status code='%s' level='%s' " - "ex.code=%ui ex.redirect='%s'", - "NetConnection.Connect.Rejected", "Error", 302, to_url.data); + "create redirect status: method='%s', status code='%s' level='%s' " + "ex.code=%ui ex.redirect='%s'", callMethod, + "NetConnection.Connect.Rejected", "error", 302, to_url.data); static ngx_rtmp_amf_elt_t out_inf_ex_data[] = { @@ -624,12 +625,12 @@ ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *desc, ngx_str_t to_ static ngx_rtmp_amf_elt_t out_inf[] = { { NGX_RTMP_AMF_STRING, - ngx_string("code"), - NULL, 0 }, + ngx_string("level"), + "error", 0 }, { NGX_RTMP_AMF_STRING, - ngx_string("level"), - NULL, 0 }, + ngx_string("code"), + "NetConnection.Connect.Rejected", 0 }, { NGX_RTMP_AMF_STRING, ngx_string("description"), @@ -645,18 +646,18 @@ ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *desc, ngx_str_t to_ { NGX_RTMP_AMF_STRING, ngx_null_string, - "onStatus", 0 }, + NULL, 0 }, { NGX_RTMP_AMF_NUMBER, ngx_null_string, - &trans, 0 }, + &dtrans, 0 }, { NGX_RTMP_AMF_NULL, ngx_null_string, NULL, 0 }, { NGX_RTMP_AMF_OBJECT, - ngx_string("info"), + ngx_null_string, out_inf, sizeof(out_inf) }, }; @@ -664,15 +665,15 @@ ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *desc, ngx_str_t to_ ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "create redirect status: set structure data"); - out_inf[0].data = "NetConnection.Connect.Rejected"; - out_inf[1].data = "Error"; + out_elts[0].data = callMethod; out_inf[2].data = desc; dcode = 302; + dtrans = 0; out_inf_ex_data[1].data = to_url.data; - memset(&h, 0, sizeof(h)); + ngx_memzero(&h, sizeof(h)); - h.type = NGX_RTMP_MSG_AMF_META; + h.type = NGX_RTMP_MSG_AMF_CMD; h.csid = NGX_RTMP_CSID_AMF; h.msid = NGX_RTMP_MSID; @@ -683,10 +684,52 @@ ngx_rtmp_create_redirect_status(ngx_rtmp_session_t *s, char *desc, ngx_str_t to_ ngx_int_t ngx_rtmp_send_redirect_status(ngx_rtmp_session_t *s, - char *desc, ngx_str_t to_url) + char *callMethod, char *desc, ngx_str_t to_url) { return ngx_rtmp_send_shared_packet(s, - ngx_rtmp_create_redirect_status(s, desc, to_url)); + ngx_rtmp_create_redirect_status(s, callMethod, desc, to_url)); +} + + +ngx_chain_t * +ngx_rtmp_create_close_method(ngx_rtmp_session_t *s, char *methodName) +{ + ngx_rtmp_header_t h; + static double dtrans; + + static ngx_rtmp_amf_elt_t out_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_null_string, + NULL, 0 }, + + { NGX_RTMP_AMF_NUMBER, + ngx_null_string, + &dtrans, 0 }, + }; + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "create close method: set structure data"); + + out_elts[0].data = methodName; + dtrans = 0; + + ngx_memzero(&h, sizeof(h)); + + h.type = NGX_RTMP_MSG_AMF_CMD; + h.csid = NGX_RTMP_CSID_AMF; + h.msid = NGX_RTMP_MSID; + + return ngx_rtmp_create_amf(s, &h, out_elts, + sizeof(out_elts) / sizeof(out_elts[0])); +} + + +ngx_int_t +ngx_rtmp_send_close_method(ngx_rtmp_session_t *s, char *methodName) +{ + return ngx_rtmp_send_shared_packet(s, + ngx_rtmp_create_close_method(s, methodName)); }