static void ngx_rtmp_ssl_handshake_handler(ngx_connection_t *c) { ngx_rtmp_session_t *s; ngx_event_t *rev; ngx_int_t rc; s = c->data; if (c->ssl->handshaked) { ngx_rtmp_handshake(s); return; } if (c->read->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "SSL handshake timed out"); ngx_rtmp_finalize_session(s); return; } if (c->read->error || c->write->error || c->error) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "SSL handshake failed: c%d w%d r%d", c->error, c->write->error, c->read->error); ngx_rtmp_finalize_session(s); return; } rc = ngx_ssl_handshake(c); if (rc == NGX_AGAIN) { rev = c->read; if (!rev->timer_set) { ngx_add_timer(rev, s->timeout); } c->ssl->handler = ngx_rtmp_ssl_handshake_handler; return; } if (rc == NGX_OK) { ngx_rtmp_handshake(s); return; } ngx_rtmp_finalize_session(s); return; }
static void ngx_live_relay_static_handler(ngx_event_t *ev) { ngx_rtmp_session_t *s; ngx_live_relay_app_conf_t *lracf; ngx_live_relay_ctx_t *ctx; ngx_live_relay_static_ctx_t *sctx; ngx_live_relay_t *relay; s = ev->data; ctx = ngx_rtmp_get_module_ctx(s, ngx_live_relay_module); if (!ctx->failed_delay && ev->timedout) { // connect timeout ngx_log_error(NGX_LOG_ERR, s->log, NGX_ETIMEDOUT, "static relay, relay timeout"); s->finalize_reason = NGX_LIVE_RELAY_TIMEOUT; ngx_rtmp_finalize_session(s); return; } lracf = ngx_rtmp_get_module_app_conf(s, ngx_live_relay_module); ngx_add_timer(&ctx->reconnect, lracf->relay_reconnect); sctx = ngx_rtmp_get_module_ctx(s, ngx_live_relay_static_module); relay = sctx->relay->relay; ngx_live_relay_create(s, relay); }
void ngx_rtmp_proxy_protocol(ngx_rtmp_session_t *s) { ngx_event_t *rev; ngx_connection_t *c; c = s->connection; rev = c->read; rev->handler = ngx_rtmp_proxy_protocol_recv; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "proxy_protocol: start"); if (rev->ready) { /* the deferred accept(), rtsig, aio, iocp */ if (ngx_use_accept_mutex) { ngx_post_event(rev, &ngx_posted_events); return; } rev->handler(rev); return; } ngx_add_timer(rev, s->timeout); if (ngx_handle_read_event(rev, 0) != NGX_OK) { ngx_rtmp_finalize_session(s); return; } }
void ngx_rtmp_client_handshake(ngx_rtmp_session_t *s, unsigned async) { ngx_connection_t *c; c = s->connection; c->read->handler = ngx_rtmp_handshake_recv; c->write->handler = ngx_rtmp_handshake_send; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "handshake: start client handshake"); s->hs_buf = ngx_rtmp_alloc_handshake_buffer(s); s->hs_stage = NGX_RTMP_HANDSHAKE_CLIENT_SEND_CHALLENGE; if (ngx_rtmp_handshake_create_challenge(s, ngx_rtmp_client_version, &ngx_rtmp_client_partial_key) != NGX_OK) { ngx_rtmp_finalize_session(s); return; } if (async) { ngx_post_event(c->write, &ngx_posted_events); return; } ngx_rtmp_handshake_send(c->write); }
void ngx_rtmp_client_handshake(ngx_rtmp_session_t *s, unsigned async) { ngx_connection_t *c; c = s->connection; c->read->handler = ngx_rtmp_handshake_recv; c->write->handler = ngx_rtmp_handshake_send; ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "handshake: start client handshake, addr_text=%V', tcurl='%V'", s->addr_text, &s->tc_url); s->hs_buf = ngx_rtmp_alloc_handshake_buffer(s); s->hs_stage = NGX_RTMP_HANDSHAKE_CLIENT_SEND_CHALLENGE; if (ngx_rtmp_handshake_create_challenge(s, ngx_rtmp_client_version, &ngx_rtmp_client_partial_key) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "handshake: ngx_rtmp_handshake_create_challenge failed, addr_text=%V', tcurl='%V'", s->addr_text, &s->tc_url); ngx_rtmp_finalize_session(s); return; } if (async) { ngx_add_timer(c->write, s->timeout); if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "handshake: ngx_handle_write_event failed, addr_text=%V', tcurl='%V'", s->addr_text, &s->tc_url); ngx_rtmp_finalize_session(s); } return; } ngx_rtmp_handshake_send(c->write); }
ngx_int_t ngx_live_relay_create_httpflv(ngx_rtmp_session_t *s, ngx_live_relay_t *relay, ngx_live_relay_url_t *url) { ngx_live_relay_ctx_t *rctx; // must use ngx_sockaddr_t, because sizeof(struct sockaddr) // is not long enouph, content will be covered by other var ngx_sockaddr_t nsa; struct sockaddr *sa; socklen_t len; rctx = ngx_rtmp_get_module_ctx(s, ngx_live_relay_module); if (rctx == NULL) { return NGX_ERROR; } #define NGX_LIVE_RELAY_CTX(para) \ if (ngx_copy_str(s->pool, &rctx->para, &relay->para) != NGX_OK) { \ goto destroy; \ } NGX_LIVE_RELAY_CTX(domain); NGX_LIVE_RELAY_CTX(app); NGX_LIVE_RELAY_CTX(name); NGX_LIVE_RELAY_CTX(pargs); NGX_LIVE_RELAY_CTX(referer); NGX_LIVE_RELAY_CTX(user_agent); #undef NGX_LIVE_RELAY_CTX rctx->tag = relay->tag; // get address, host in url must be resolv sync sa = (struct sockaddr *) &nsa; len = ngx_dynamic_resolver_gethostbyname(&url->url.host, sa); if (len == 0) { ngx_log_error(NGX_LOG_ERR, s->log, 0, "relay httpflv: gethostbyname failed %V", &url->url.host); goto destroy; } // send http request if (ngx_live_relay_httpflv_send_request(s, url) != NGX_OK) { goto destroy; } return NGX_OK; destroy: ngx_rtmp_finalize_session(s); return NGX_ERROR; }
static ngx_int_t ngx_rtmp_relay_create(ngx_rtmp_session_t *s, ngx_str_t *name, ngx_rtmp_relay_target_t *target, ngx_rtmp_relay_create_ctx_pt create_publish_ctx, ngx_rtmp_relay_create_ctx_pt create_play_ctx) { ngx_rtmp_relay_app_conf_t *racf; ngx_rtmp_relay_ctx_t *publish_ctx, *play_ctx, **cctx; ngx_uint_t hash; racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module); if (racf == NULL) { return NGX_ERROR; } play_ctx = create_play_ctx(s, name, target); if (play_ctx == NULL) { return NGX_ERROR; } hash = ngx_hash_key(name->data, name->len); cctx = &racf->ctx[hash % racf->nbuckets]; for (; *cctx; cctx = &(*cctx)->next) { if ((*cctx)->name.len == name->len && !ngx_memcmp(name->data, (*cctx)->name.data, name->len)) { break; } } if (*cctx) { play_ctx->publish = (*cctx)->publish; play_ctx->next = (*cctx)->play; (*cctx)->play = play_ctx; return NGX_OK; } publish_ctx = create_publish_ctx(s, name, target); if (publish_ctx == NULL) { ngx_rtmp_finalize_session(play_ctx->session); return NGX_ERROR; } publish_ctx->publish = publish_ctx; publish_ctx->play = play_ctx; play_ctx->publish = publish_ctx; *cctx = publish_ctx; return NGX_OK; }
/* 空闲检查定时器回调处理 */ static void ngx_rtmp_live_idle(ngx_event_t *pev) { ngx_connection_t *c; ngx_rtmp_session_t *s; c = pev->data; s = c->data; ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "live: drop idle publisher"); ngx_rtmp_finalize_session(s); }
static void ngx_rtmp_handshake_done(ngx_rtmp_session_t *s) { ngx_rtmp_free_handshake_buffers(s); ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "handshake: done"); if (ngx_rtmp_fire_event(s, NGX_RTMP_HANDSHAKE_DONE, NULL, NULL) != NGX_OK) { ngx_rtmp_finalize_session(s); return; } ngx_rtmp_cycle(s); }
static void ngx_rtmp_netcall_close(ngx_connection_t *cc) { ngx_rtmp_netcall_session_t *cs, **css; ngx_pool_t *pool; ngx_rtmp_session_t *s; ngx_rtmp_netcall_ctx_t *ctx; ngx_buf_t *b; cs = cc->data; if (cc->destroyed) { return; } cc->destroyed = 1; if (!cs->detached) { s = cs->session; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_netcall_module); if (cs->in && cs->sink) { cs->sink(cs->session, cs->in); b = cs->in->buf; b->pos = b->last = b->start; } for(css = &ctx->cs; *css; css = &((*css)->next)) { if (*css == cs) { *css = cs->next; break; } } if (cs->handle && cs->handle(s, cs->arg, cs->in) != NGX_OK) { ngx_rtmp_finalize_session(s); } } pool = cc->pool; ngx_close_connection(cc); ngx_destroy_pool(pool); }
void ngx_rtmp_ssl_handshake(ngx_rtmp_session_t *s) { ngx_connection_t *c; ngx_rtmp_ssl_srv_conf_t *sscf; c = s->connection; sscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_ssl_module); if (ngx_ssl_create_connection(&sscf->ssl, c, 0) != NGX_OK) { ngx_rtmp_finalize_session(s); return; } ngx_rtmp_ssl_handshake_handler(c); return; }
static const char * ngx_rtmp_control_drop_handler(ngx_http_request_t *r, ngx_rtmp_session_t *s, ngx_rtmp_core_srv_conf_t *cscf, ngx_rtmp_core_app_conf_t **cacf) { ngx_rtmp_control_ctx_t *ctx; ctx = ngx_http_get_module_ctx(r, ngx_rtmp_control_module); ngx_log_debug(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "control: drop static_relay='%d'", s->static_relay); s->static_relay = 0; ngx_rtmp_finalize_session(s); ++ctx->count; return NGX_CONF_OK; }
static void ngx_rtmp_log_pre_write(ngx_rtmp_session_t *s, ngx_rtmp_log_t *log) { ngx_rtmp_log_op_t *op; u_char line[MAX_ACCESS_LOG_LINE_LEN], *p; size_t len; ngx_uint_t n; if (ngx_time() == log->disk_full_time) { /* FreeBSD full disk protection; * nginx http logger does the same */ return; } len = 0; op = log->format->ops->elts; for (n = 0; n < log->format->ops->nelts; ++n, ++op) { len += op->getlen(s, op); } len += NGX_LINEFEED_SIZE; if (len > MAX_ACCESS_LOG_LINE_LEN) { ngx_log_error(NGX_LOG_ERR, s->log, 0, "Access line len %z greater than %d", len, MAX_ACCESS_LOG_LINE_LEN); ngx_rtmp_finalize_session(s); return; } p = line; op = log->format->ops->elts; for (n = 0; n < log->format->ops->nelts; ++n, ++op) { p = op->getdata(s, p, op); } ngx_linefeed(p); ngx_rtmp_log_write(s, log, line, p - line); }
static void ngx_live_relay_inner_handler(ngx_event_t *ev) { ngx_rtmp_session_t *s; ngx_live_relay_app_conf_t *lracf; ngx_live_relay_ctx_t *ctx; ngx_live_relay_t relay; s = ev->data; ctx = ngx_rtmp_get_module_ctx(s, ngx_live_relay_module); if (!ctx->failed_delay && ev->timedout) { // connect timeout ngx_log_error(NGX_LOG_ERR, s->log, NGX_ETIMEDOUT, "inner relay, relay timeout"); s->finalize_reason = NGX_LIVE_RELAY_TIMEOUT; ngx_rtmp_finalize_session(s); return; } // relay pull, no player or relay push no publisher if ((s->publishing && s->live_stream->play_ctx == NULL) || (!s->publishing && s->live_stream->publish_ctx == NULL)) { return; } lracf = ngx_rtmp_get_module_app_conf(s, ngx_live_relay_module); ngx_add_timer(&ctx->reconnect, lracf->relay_reconnect); if (ngx_live_relay_inner_create_relay(s, &relay, s->live_stream->pslot) != NGX_OK) { return; } ngx_live_relay_create(s, &relay); }
static void ngx_live_relay_httpflv_error(ngx_rtmp_session_t *s, ngx_uint_t status) { ngx_live_stream_t *st; ngx_rtmp_core_ctx_t *cctx; char *code, *level, *desc; size_t i; for (i = 0; ngx_http_relay_status_code[i].status; ++i) { if (status != ngx_http_relay_status_code[i].status) { continue; } break; } code = ngx_http_relay_status_code[i].code; level = ngx_http_relay_status_code[i].level; desc = ngx_http_relay_status_code[i].desc; ngx_log_error(NGX_LOG_ERR, s->log, 0, "http relay transit, %d: level='%s' code='%s' description='%s'", status, level, code, desc); st = ngx_live_create_stream(&s->serverid, &s->stream); cctx = st->play_ctx; for (; cctx; cctx = cctx->next) { cctx->session->status = status; ngx_rtmp_send_status(cctx->session, code, level, desc); if (ngx_strcmp(level, "error") == 0 && !cctx->session->static_pull) { cctx->session->finalize_reason = NGX_LIVE_RELAY_TRANSIT; ngx_rtmp_finalize_session(cctx->session); } } }
static ngx_int_t ngx_rtmp_relay_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v) { ngx_rtmp_relay_app_conf_t *racf; ngx_rtmp_relay_ctx_t *ctx, **cctx; ngx_uint_t hash; racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_relay_module); if (ctx == NULL || ctx->publish == NULL) { goto next; } /* play end disconnect? */ if (ctx->publish != ctx) { for (cctx = &ctx->publish->play; *cctx; cctx = &(*cctx)->next) { if (*cctx == ctx) { *cctx = ctx->next; break; } } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0, "relay: play disconnect app='%V' name='%V'", &ctx->app, &ctx->name); /* push reconnect */ if (ctx->relay && ctx->tag == &ngx_rtmp_relay_module && !ctx->publish->push_evt.timer_set) { ngx_add_timer(&ctx->publish->push_evt, racf->push_reconnect); } #ifdef NGX_DEBUG { ngx_uint_t n = 0; for (cctx = &ctx->publish->play; *cctx; cctx = &(*cctx)->next, ++n); ngx_log_debug3(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0, "relay: play left after disconnect app='%V' name='%V': %ui", &ctx->app, &ctx->name, n); } #endif if (ctx->publish->play == NULL && ctx->publish->relay) { ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->publish->session->connection->log, 0, "relay: publish disconnect empty app='%V' name='%V'", &ctx->app, &ctx->name); ngx_rtmp_finalize_session(ctx->publish->session); } ctx->publish = NULL; goto next; } /* publish end disconnect */ ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ctx->session->connection->log, 0, "relay: publish disconnect app='%V' name='%V'", &ctx->app, &ctx->name); if (ctx->push_evt.timer_set) { ngx_del_timer(&ctx->push_evt); } for (cctx = &ctx->play; *cctx; cctx = &(*cctx)->next) { (*cctx)->publish = NULL; ngx_log_debug2(NGX_LOG_DEBUG_RTMP, (*cctx)->session->connection->log, 0, "relay: play disconnect orphan app='%V' name='%V'", &(*cctx)->app, &(*cctx)->name); ngx_rtmp_finalize_session((*cctx)->session); } ctx->publish = NULL; hash = ngx_hash_key(ctx->name.data, ctx->name.len); cctx = &racf->ctx[hash % racf->nbuckets]; for (; *cctx && *cctx != ctx; cctx = &(*cctx)->next); if (*cctx) { *cctx = ctx->next; } next: return next_delete_stream(s, v); }
/* rtmp session加入直播流 */ static void ngx_rtmp_live_join(ngx_rtmp_session_t *s, u_char *name, unsigned publisher) { ngx_rtmp_live_ctx_t *ctx; ngx_rtmp_live_stream_t **stream; ngx_rtmp_live_app_conf_t *lacf; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL) { return; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx && ctx->stream) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: already joined"); return; } if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_live_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_live_module); } ngx_memzero(ctx, sizeof(*ctx)); ctx->session = s; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: join '%s'", name); /* 直播流 用name作key */ stream = ngx_rtmp_live_get_stream(s, name, publisher || lacf->idle_streams); if (stream == NULL || !(publisher || (*stream)->publishing || lacf->idle_streams)) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "live: stream not found"); ngx_rtmp_send_status(s, "NetStream.Play.StreamNotFound", "error", "No such stream"); ngx_rtmp_finalize_session(s); return; } if (publisher) { if ((*stream)->publishing) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "live: already publishing"); ngx_rtmp_send_status(s, "NetStream.Publish.BadName", "error", "Already publishing"); return; } (*stream)->publishing = 1; } ctx->stream = *stream; /* 加入直播流指针 */ ctx->publishing = publisher; /* 加入直播流 ctx链表 */ ctx->next = (*stream)->ctx; (*stream)->ctx = ctx; if (lacf->buflen) { s->out_buffer = 1; } ctx->cs[0].csid = NGX_RTMP_CSID_VIDEO; ctx->cs[1].csid = NGX_RTMP_CSID_AUDIO; /* 发布已经开启,开启播放 */ if (!ctx->publishing && ctx->stream->active) { ngx_rtmp_live_start(s); } }
static ngx_int_t ngx_live_relay_static_relay(ngx_rtmp_session_t *s, ngx_live_relay_static_relay_t *r) { ngx_rtmp_session_t *rs; ngx_live_relay_ctx_t *ctx, *pctx; ngx_live_relay_app_conf_t *lracf; ngx_live_relay_static_main_conf_t *rsmcf; ngx_live_relay_static_ctx_t *sctx; ngx_live_relay_t *relay; ngx_rtmp_addr_conf_t *addr_conf; relay = r->relay; rsmcf = ngx_rtmp_cycle_get_module_main_conf(ngx_cycle, ngx_live_relay_static_module); addr_conf = ngx_rtmp_find_related_addr_conf((ngx_cycle_t *) ngx_cycle, &rsmcf->pull_port); if (addr_conf == NULL) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "relay static, find related add_conf for %V failed", &rsmcf->pull_port); return NGX_DECLINED; } rs = ngx_rtmp_create_static_session(relay, addr_conf, &ngx_live_relay_static_module); if (rs == NULL) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, 0, "relay static, create relay session %V failed", &relay->stream); return NGX_DECLINED; } r->session = rs; rs->publishing = 1; rs->live_stream = ngx_live_create_stream(&rs->domain, &rs->stream); ngx_live_create_ctx(rs, 1); sctx = ngx_pcalloc(rs->pool, sizeof(ngx_live_relay_static_ctx_t)); if (sctx == NULL) { ngx_log_error(NGX_LOG_ERR, rs->log, 0, "relay static, create static relay ctx failed"); ngx_rtmp_finalize_session(rs); return NGX_OK; } ngx_rtmp_set_ctx(rs, sctx, ngx_live_relay_static_module); sctx->relay = r; ctx = ngx_rtmp_get_module_ctx(rs, ngx_live_relay_module); ctx->reconnect.log = rs->log; ctx->reconnect.data = rs; ctx->reconnect.handler = ngx_live_relay_static_handler; if (s == NULL) { ngx_post_event(&ctx->reconnect, &ngx_posted_events); return NGX_OK; } // reconnect pctx = ngx_rtmp_get_module_ctx(s, ngx_live_relay_module); if (pctx->successd) { // prev relay successd ngx_post_event(&ctx->reconnect, &ngx_posted_events); return NGX_OK; } ctx->idx = pctx->idx; ctx->failed_reconnect = pctx->failed_reconnect; if (ctx->idx < relay->urls.nelts) { // retry backup url immediately ngx_post_event(&ctx->reconnect, &ngx_posted_events); return NGX_OK; } lracf = ngx_rtmp_get_module_app_conf(rs, ngx_live_relay_module); if (!pctx->reconnect.timer_set) { // prev relay timeout ctx->failed_reconnect = ngx_min(pctx->failed_reconnect * 2, lracf->relay_reconnect); ngx_post_event(&ctx->reconnect, &ngx_posted_events); return NGX_OK; } if (pctx->failed_reconnect) { ctx->failed_reconnect = ngx_min(pctx->failed_reconnect * 2, lracf->relay_reconnect); } else { ctx->failed_reconnect = lracf->failed_reconnect; } ctx->failed_delay = 1; ngx_add_timer(&ctx->reconnect, ctx->failed_reconnect); return NGX_OK; }
// merge all static pull into ngx_live_relay_static_main_conf_t; static char * ngx_live_relay_static_init_main_dconf(ngx_conf_t *cf, void *conf) { ngx_live_relay_static_main_conf_t *rsmcf; ngx_live_relay_static_main_dconf_t *rsmdcf; ngx_core_conf_t *ccf; ngx_live_relay_t *relay; ngx_live_relay_static_relay_t *srelay, *old, *sl, *sln, **sll; ngx_live_relay_static_ctx_t *ctx; ngx_live_relay_ctx_t *rctx; ngx_map_node_t *node; unsigned used; char *rc; ngx_uint_t i, hash; rsmdcf = conf; rsmcf = ngx_rtmp_cycle_get_module_main_conf(ngx_cycle, ngx_live_relay_static_module); ccf = (ngx_core_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, ngx_core_module); used = rsmcf->used? 0: 1; sl = NULL; sll = &sl; relay = rsmdcf->pulls.elts; for (i = 0; i < rsmdcf->pulls.nelts; ++i, ++relay) { // should static pull in current process? if (ngx_process == NGX_PROCESS_WORKER) { hash = ngx_hash_key_lc(relay->stream.data, relay->stream.len); if (hash % ccf->worker_processes != ngx_worker) { continue; } } // check static pull duplicate node = ngx_map_find(&rsmcf->pulls[used], (intptr_t) &relay->stream); if (node) { rc = "duplicate static pull"; goto error; } srelay = ngx_live_relay_get_static_relay(rsmcf); if (srelay == NULL) { rc = "get static relay failed"; goto error; } srelay->relay = relay; srelay->node.raw_key = (intptr_t) &relay->stream; ngx_map_insert(&rsmcf->pulls[used], &srelay->node, 0); // check static pull is exist node = ngx_map_find(&rsmcf->pulls[rsmcf->used], (intptr_t) &relay->stream); if (node) { old = (ngx_live_relay_static_relay_t *) node; srelay->session = old->session; // link swap static pull *sll = old; sll = &(*sll)->next; } } // delete swap static pull from old while (sl) { sln = sl; sl = sl->next; ngx_map_delete(&rsmcf->pulls[rsmcf->used], (intptr_t) &sln->relay->stream); ngx_live_relay_put_static_relay(rsmcf, sln); } // stop deleted static pull node = ngx_map_begin(&rsmcf->pulls[rsmcf->used]); while (node) { srelay = (ngx_live_relay_static_relay_t *) node; node = ngx_map_next(node); ngx_live_relay_put_static_relay(rsmcf, srelay); rctx = ngx_rtmp_get_module_ctx(srelay->session, ngx_live_relay_module); rctx->giveup = 1; srelay->session->finalize_reason = NGX_LIVE_NORMAL_CLOSE; ngx_rtmp_finalize_session(srelay->session); ngx_map_delete(&rsmcf->pulls[rsmcf->used], (intptr_t) &srelay->relay->stream); } // new static relay node = ngx_map_begin(&rsmcf->pulls[used]); for (; node; node = ngx_map_next(node)) { srelay = (ngx_live_relay_static_relay_t *) node; if (srelay->session == NULL) { ngx_live_relay_static_relay(NULL, srelay); } else { ctx = ngx_rtmp_get_module_ctx(srelay->session, ngx_live_relay_static_module); ctx->relay = srelay; } } rsmcf->used = used; return NGX_CONF_OK; error: // recycle static relay resource node = ngx_map_begin(&rsmcf->pulls[used]); while (node) { srelay = (ngx_live_relay_static_relay_t *) node; node = ngx_map_next(node); ngx_live_relay_put_static_relay(rsmcf, srelay); ngx_map_delete(&rsmcf->pulls[used], (intptr_t) &srelay->relay->stream); } return rc; }
ngx_rtmp_session_t * ngx_rtmp_init_session(ngx_connection_t *c, ngx_rtmp_addr_conf_t *addr_conf) { ngx_rtmp_session_t *s; ngx_rtmp_core_srv_conf_t *cscf; ngx_rtmp_error_log_ctx_t *ctx; s = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_session_t) + sizeof(ngx_chain_t *) * ((ngx_rtmp_core_srv_conf_t *) addr_conf->ctx-> srv_conf[ngx_rtmp_core_module .ctx_index])->out_queue); if (s == NULL) { ngx_rtmp_close_connection(c); return NULL; } s->main_conf = addr_conf->ctx->main_conf; s->srv_conf = addr_conf->ctx->srv_conf; s->addr_text = &addr_conf->addr_text; c->data = s; s->connection = c; ctx = ngx_palloc(c->pool, sizeof(ngx_rtmp_error_log_ctx_t)); if (ctx == NULL) { ngx_rtmp_close_connection(c); return NULL; } ctx->client = &c->addr_text; ctx->session = s; c->log->connection = c->number; c->log->handler = ngx_rtmp_log_error; c->log->data = ctx; c->log->action = NULL; c->log_error = NGX_ERROR_INFO; s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_rtmp_max_module); if (s->ctx == NULL) { ngx_rtmp_close_connection(c); return NULL; } cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module); s->out_queue = cscf->out_queue; s->out_cork = cscf->out_cork; s->in_streams = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_stream_t) * cscf->max_streams); if (s->in_streams == NULL) { ngx_rtmp_close_connection(c); return NULL; } s->epoch = ngx_current_msec; s->timeout = cscf->timeout; ngx_rtmp_set_chunk_size(s, NGX_RTMP_DEFAULT_CHUNK_SIZE); if (ngx_rtmp_fire_event(s, NGX_RTMP_CONNECT, NULL, NULL) != NGX_OK) { ngx_rtmp_finalize_session(s); return NULL; } return s; }
static ngx_int_t ngx_rtmp_live_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) { ngx_rtmp_session_t *ss; ngx_rtmp_live_ctx_t *ctx, **cctx, *pctx; ngx_rtmp_live_stream_t **stream; ngx_rtmp_live_app_conf_t *lacf; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL) { goto next; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL) { goto next; } if (ctx->stream == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: not joined"); goto next; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: leave '%s'", ctx->stream->name); if (ctx->stream->publishing && ctx->publishing) { ctx->stream->publishing = 0; } for (cctx = &ctx->stream->ctx; *cctx; cctx = &(*cctx)->next) { if (*cctx == ctx) { *cctx = ctx->next; break; } } if (ctx->publishing || ctx->stream->active) { ngx_rtmp_live_stop(s); } if (ctx->publishing) { ngx_rtmp_send_status(s, "NetStream.Unpublish.Success", "status", "Stop publishing"); if (!lacf->idle_streams) { for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) { if (pctx->publishing == 0) { ss = pctx->session; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: no publisher"); ngx_rtmp_finalize_session(ss); } } } } if (ctx->stream->ctx) { ctx->stream = NULL; goto next; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: delete empty stream '%s'", ctx->stream->name); stream = ngx_rtmp_live_get_stream(s, ctx->stream->name, 0); if (stream == NULL) { goto next; } *stream = (*stream)->next; ctx->stream->next = lacf->free_streams; lacf->free_streams = ctx->stream; ctx->stream = NULL; if (!ctx->silent && !ctx->publishing && !lacf->play_restart) { ngx_rtmp_send_status(s, "NetStream.Play.Stop", "status", "Stop live"); } next: return next_close_stream(s, v); }
/* 直播音视频处理 */ static ngx_int_t ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_rtmp_live_ctx_t *ctx, *pctx; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_chain_t *header, *coheader, *meta, *apkt, *aapkt, *acopkt, *rpkt; ngx_rtmp_core_srv_conf_t *cscf; ngx_rtmp_live_app_conf_t *lacf; ngx_rtmp_session_t *ss; ngx_rtmp_header_t ch; /* 当前rtmp header */ ngx_rtmp_header_t lh; /* 上一个rtmp header*/ ngx_rtmp_header_t clh; /* 音频转化视频的rtmp header */ ngx_int_t rc, mandatory, dummy_audio; ngx_uint_t prio; ngx_uint_t peers; ngx_uint_t meta_version; ngx_uint_t csidx; uint32_t delta; ngx_rtmp_live_chunk_stream_t *cs; #ifdef NGX_DEBUG const char *type_s; type_s = (h->type == NGX_RTMP_MSG_VIDEO ? "video" : "audio"); #endif lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL) { return NGX_ERROR; } if (!lacf->live || in == NULL || in->buf == NULL) { return NGX_OK; } /* 从会话中获取live ctx */ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL || ctx->stream == NULL) { return NGX_OK; } if (ctx->publishing == 0) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: %s from non-publisher", type_s); return NGX_OK; } if (!ctx->stream->active) { ngx_rtmp_live_start(s); } if (ctx->idle_evt.timer_set) { ngx_add_timer(&ctx->idle_evt, lacf->idle_timeout); } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: %s packet timestamp=%uD", type_s, h->timestamp); s->current_time = h->timestamp; peers = 0; apkt = NULL; aapkt = NULL; acopkt = NULL; header = NULL; coheader = NULL; meta = NULL; meta_version = 0; mandatory = 0; prio = (h->type == NGX_RTMP_MSG_VIDEO ? ngx_rtmp_get_video_frame_type(in) : 0); cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module); csidx = !(lacf->interleave || h->type == NGX_RTMP_MSG_VIDEO); /* 视频 csidx为0 音频csidx为0 */ cs = &ctx->cs[csidx]; ngx_memzero(&ch, sizeof(ch)); ch.timestamp = h->timestamp; /* 从接收报文中获取时间戳 */ ch.msid = NGX_RTMP_MSID; ch.csid = cs->csid; ch.type = h->type; /* 从接收报文中获取类型 */ lh = ch; if (cs->active) { lh.timestamp = cs->timestamp; /* 从流中获取时戳 */ } clh = lh; clh.type = (h->type == NGX_RTMP_MSG_AUDIO ? NGX_RTMP_MSG_VIDEO : NGX_RTMP_MSG_AUDIO); /* 如果是音频与视频相互转换,类型一定是视频 */ cs->active = 1; cs->timestamp = ch.timestamp; /* 更新时戳 */ delta = ch.timestamp - lh.timestamp; /* if (delta >> 31) { ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: clipping non-monotonical timestamp %uD->%uD", lh.timestamp, ch.timestamp); delta = 0; ch.timestamp = lh.timestamp; } */ rpkt = ngx_rtmp_append_shared_bufs(cscf, NULL, in); ngx_rtmp_prepare_message(s, &ch, &lh, rpkt); codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); /* 转码处理 */ if (codec_ctx) { if (h->type == NGX_RTMP_MSG_AUDIO) { header = codec_ctx->aac_header; if (lacf->interleave) { coheader = codec_ctx->avc_header; } if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC && ngx_rtmp_is_codec_header(in)) { prio = 0; mandatory = 1; } } else { header = codec_ctx->avc_header; if (lacf->interleave) { coheader = codec_ctx->aac_header; } if (codec_ctx->video_codec_id == NGX_RTMP_VIDEO_H264 && ngx_rtmp_is_codec_header(in)) { prio = 0; mandatory = 1; } } if (codec_ctx->meta) { meta = codec_ctx->meta; meta_version = codec_ctx->meta_version; } } /* broadcast to all subscribers */ for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) { if (pctx == ctx || pctx->paused) { /* 发布rtmp流本身或者该播放流已中止 */ continue; } ss = pctx->session; cs = &pctx->cs[csidx]; /* send metadata */ /* 发送元数据 */ if (meta && meta_version != pctx->meta_version) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: meta"); if (ngx_rtmp_send_message(ss, meta, 0) == NGX_OK) { pctx->meta_version = meta_version; } } /* sync stream */ if (cs->active && (lacf->sync && cs->dropped > lacf->sync)) { ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: sync %s dropped=%uD", type_s, cs->dropped); cs->active = 0; cs->dropped = 0; } /* absolute packet */ if (!cs->active) { /* 流没有启动 */ if (mandatory) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: skipping header"); continue; } /* 等待视频 */ if (lacf->wait_video && h->type == NGX_RTMP_MSG_AUDIO && !pctx->cs[0].active) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: waiting for video"); continue; } /* 等待关键帧 */ if (lacf->wait_key && prio != NGX_RTMP_VIDEO_KEY_FRAME && (lacf->interleave || h->type == NGX_RTMP_MSG_VIDEO)) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: skip non-key"); continue; } dummy_audio = 0; /* 生成dummy audio */ if (lacf->wait_video && h->type == NGX_RTMP_MSG_VIDEO && !pctx->cs[1].active) { dummy_audio = 1; if (aapkt == NULL) { aapkt = ngx_rtmp_alloc_shared_buf(cscf); /* aapk如果申请失败,需要作一下失败处理,避免访问空指针 */ if(NULL != aapkt){ ngx_rtmp_prepare_message(s, &clh, NULL, aapkt); } } } /* 转码 */ if (header || coheader) { /* send absolute codec header */ ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: abs %s header timestamp=%uD", type_s, lh.timestamp); if (header) { if (apkt == NULL) { apkt = ngx_rtmp_append_shared_bufs(cscf, NULL, header); ngx_rtmp_prepare_message(s, &lh, NULL, apkt); } rc = ngx_rtmp_send_message(ss, apkt, 0); if (rc != NGX_OK) { continue; } } if (coheader) { if (acopkt == NULL) { acopkt = ngx_rtmp_append_shared_bufs(cscf, NULL, coheader); ngx_rtmp_prepare_message(s, &clh, NULL, acopkt); } rc = ngx_rtmp_send_message(ss, acopkt, 0); if (rc != NGX_OK) { continue; } } else if (dummy_audio) { ngx_rtmp_send_message(ss, aapkt, 0); } cs->timestamp = lh.timestamp; cs->active = 1; ss->current_time = cs->timestamp; } else { /* send absolute packet */ ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: abs %s packet timestamp=%uD", type_s, ch.timestamp); if (apkt == NULL) { apkt = ngx_rtmp_append_shared_bufs(cscf, NULL, in); ngx_rtmp_prepare_message(s, &ch, NULL, apkt); } rc = ngx_rtmp_send_message(ss, apkt, prio); if (rc != NGX_OK) { continue; } cs->timestamp = ch.timestamp; cs->active = 1; ss->current_time = cs->timestamp; ++peers; if (dummy_audio) { ngx_rtmp_send_message(ss, aapkt, 0); } continue; } } /* send relative packet */ ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: rel %s packet delta=%uD", type_s, delta); if (ngx_rtmp_send_message(ss, rpkt, prio) != NGX_OK) { ++pctx->ndropped; cs->dropped += delta; if (mandatory) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: mandatory packet failed"); ngx_rtmp_finalize_session(ss); } continue; } cs->timestamp += delta; ++peers; ss->current_time = cs->timestamp; } if (rpkt) { ngx_rtmp_free_shared_chain(cscf, rpkt); } if (apkt) { ngx_rtmp_free_shared_chain(cscf, apkt); } if (aapkt) { ngx_rtmp_free_shared_chain(cscf, aapkt); } if (acopkt) { ngx_rtmp_free_shared_chain(cscf, acopkt); } /* 更新直播流带宽信息 */ ngx_rtmp_update_bandwidth(&ctx->stream->bw_in, h->mlen); ngx_rtmp_update_bandwidth(&ctx->stream->bw_out, h->mlen * peers); ngx_rtmp_update_bandwidth(h->type == NGX_RTMP_MSG_AUDIO ? &ctx->stream->bw_in_audio : &ctx->stream->bw_in_video, h->mlen); return NGX_OK; }
static void ngx_live_relay_httpflv_recv_body(void *request, ngx_http_request_t *hcr) { ngx_int_t n; ngx_rtmp_session_t *s; ngx_chain_t *cl, *l, *in; ngx_rtmp_header_t *h; ngx_rtmp_stream_t *st = NULL; s = request; n = ngx_http_client_read_body(hcr, &cl); if (n == 0 || n == NGX_ERROR) { s->finalize_reason = n == 0? NGX_LIVE_NORMAL_CLOSE: NGX_LIVE_FLV_RECV_ERR; ngx_log_error(NGX_LOG_INFO, s->log, ngx_errno, "http relay, recv body error"); ngx_rtmp_finalize_session(s); return; } l = cl; for (;;) { if (l && l->buf->pos == l->buf->last) { l = l->next; } if (l == NULL) { return; } n = ngx_live_relay_httpflv_parse(s, l->buf); if (n == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->log, 0, "http relay, parse flv frame failed in state %d", s->flv_state); ngx_http_client_finalize_request(hcr, 1); return; } if (n == NGX_AGAIN) { continue; } /* NGX_OK */ st = &s->in_streams[0]; h = &st->hdr; in = st->in; if (ngx_rtmp_receive_message(s, h, in) != NGX_OK) { ngx_rtmp_finalize_session(s); return; } ngx_put_chainbufs(st->in); st->in = NULL; } }
/* 关闭流 */ static ngx_int_t ngx_rtmp_live_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) { ngx_rtmp_session_t *ss; ngx_rtmp_live_ctx_t *ctx; ngx_rtmp_live_ctx_t **cctx; ngx_rtmp_live_ctx_t *pctx; ngx_rtmp_live_stream_t **stream; ngx_rtmp_live_app_conf_t *lacf; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL) { goto next; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL) { goto next; } if (ctx->stream == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: not joined"); goto next; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: leave '%s'", ctx->stream->name); if (ctx->stream->publishing && ctx->publishing) { ctx->stream->publishing = 0; } /* 从直播流中删除当前播放流 */ for (cctx = &ctx->stream->ctx; *cctx; cctx = &(*cctx)->next) { if (*cctx == ctx) { /* 为当前流,删除,指向当前流指向的下一条流 */ *cctx = ctx->next; break; } } /* 正在发布或者播放,则停止 */ if (ctx->publishing || ctx->stream->active) { ngx_rtmp_live_stop(s); } /* 如果是发布端关闭,需要关闭对应的播放流 */ if (ctx->publishing) { ngx_rtmp_send_status(s, "NetStream.Unpublish.Success", "status", "Stop publishing"); if (!lacf->idle_streams) { for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) { if (pctx->publishing == 0) { ss = pctx->session; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: no publisher"); ngx_rtmp_finalize_session(ss); } } } } if (ctx->stream->ctx) { /* 该rtmp流对应的直播流还有其他rtmp流 */ ctx->stream = NULL; goto next; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: delete empty stream '%s'", ctx->stream->name); /* 该rtmp流对应的直播流没有其他rtmp流,回收直播流内存 */ stream = ngx_rtmp_live_get_stream(s, ctx->stream->name, 0); if (stream == NULL) { goto next; } *stream = (*stream)->next; /* 暂时无使用 */ ctx->stream->next = lacf->free_streams; /* 将stream 作为头节点加入释放流链链表 */ lacf->free_streams = ctx->stream; /* 更新释放流链表地址 */ ctx->stream = NULL; /* 置NULL,不关联直播流 */ if (!ctx->silent && !ctx->publishing && !lacf->play_restart) { ngx_rtmp_send_status(s, "NetStream.Play.Stop", "status", "Stop live"); } next: return next_close_stream(s, v); }
static void ngx_rtmp_handshake_recv(ngx_event_t *rev) { ssize_t n; ngx_connection_t *c; ngx_rtmp_session_t *s; ngx_buf_t *b; c = rev->data; s = c->hls ? c->hls_data : c->data; if (c->destroyed) { return; } if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "handshake_recv: recv: client timed out"); c->timedout = 1; ngx_rtmp_finalize_session(s); return; } if (rev->timer_set) { ngx_del_timer(rev); } b = s->hs_buf; while (b->last != b->end) { n = c->recv(c, b->last, b->end - b->last); if (n == NGX_ERROR || n == 0) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "handshake_recv: c->recv error"); ngx_rtmp_finalize_session(s); return; } if (n == NGX_AGAIN) { ngx_add_timer(rev, s->timeout); if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_rtmp_finalize_session(s); ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "handshake_recv: NGX_AGAIN, ngx_rtmp_finalize_session"); } ngx_log_debug0(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "handshake_recv: NGX_AGAIN, return"); return; } b->last += n; } if (rev->active) { ngx_del_event(rev, NGX_READ_EVENT, 0); } ++s->hs_stage; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "ngx_rtmp_handshake_recv:handshake: stage %ui", s->hs_stage); switch (s->hs_stage) { case NGX_RTMP_HANDSHAKE_SERVER_SEND_CHALLENGE: if (ngx_rtmp_handshake_parse_challenge(s, &ngx_rtmp_client_partial_key, &ngx_rtmp_server_full_key) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "handshake: error parsing challenge"); ngx_rtmp_finalize_session(s); return; } if (s->hs_old) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "handshake: old-style challenge"); s->hs_buf->pos = s->hs_buf->start; s->hs_buf->last = s->hs_buf->end; } else if (ngx_rtmp_handshake_create_challenge(s, ngx_rtmp_server_version, &ngx_rtmp_server_partial_key) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "handshake: error creating challenge"); ngx_rtmp_finalize_session(s); return; } ngx_rtmp_handshake_send(c->write); break; case NGX_RTMP_HANDSHAKE_SERVER_DONE: ngx_rtmp_handshake_done(s); break; case NGX_RTMP_HANDSHAKE_CLIENT_RECV_RESPONSE: if (ngx_rtmp_handshake_parse_challenge(s, &ngx_rtmp_server_partial_key, &ngx_rtmp_client_full_key) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "handshake: error parsing challenge"); ngx_rtmp_finalize_session(s); return; } s->hs_buf->pos = s->hs_buf->last = s->hs_buf->start + 1; ngx_rtmp_handshake_recv(c->read); break; case NGX_RTMP_HANDSHAKE_CLIENT_SEND_RESPONSE: if (ngx_rtmp_handshake_create_response(s) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "handshake: response error"); ngx_rtmp_finalize_session(s); return; } ngx_rtmp_handshake_send(c->write); break; } }
static void ngx_rtmp_gop_cache_send(ngx_rtmp_session_t *s) { ngx_rtmp_session_t *rs; ngx_chain_t *pkt, *apkt, *meta, *header; ngx_rtmp_live_ctx_t *ctx, *pub_ctx; ngx_http_flv_live_ctx_t *hflctx; ngx_rtmp_gop_cache_ctx_t *gctx; ngx_rtmp_live_app_conf_t *lacf; ngx_rtmp_gop_cache_t *cache; ngx_rtmp_gop_frame_t *gf; ngx_rtmp_header_t ch, lh; ngx_uint_t meta_version; uint32_t delta; ngx_int_t csidx; ngx_rtmp_live_chunk_stream_t *cs; ngx_rtmp_live_proc_handler_t *handler; ngx_http_request_t *r; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL) { return; } /* pub_ctx saved the publisher info */ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL || ctx->stream == NULL || ctx->stream->pub_ctx == NULL || !ctx->stream->publishing) { return; } pkt = NULL; apkt = NULL; header = NULL; meta = NULL; meta_version = 0; pub_ctx = ctx->stream->pub_ctx; rs = pub_ctx->session; s->publisher = rs; handler = ngx_rtmp_live_proc_handlers[ctx->protocol]; gctx = ngx_rtmp_get_module_ctx(rs, ngx_rtmp_gop_cache_module); if (gctx == NULL) { return; } for (cache = gctx->cache_head; cache; cache = cache->next) { if (ctx->protocol == NGX_RTMP_PROTOCOL_HTTP) { r = s->data; if (r == NULL || (r->connection && r->connection->destroyed)) { return; } hflctx = ngx_http_get_module_ctx(r, ngx_http_flv_live_module); if (!hflctx->header_sent) { hflctx->header_sent = 1; ngx_http_flv_live_send_header(s); } } if (meta == NULL && meta_version != gctx->meta_version) { meta = handler->meta_message_pt(s, gctx->meta); if (meta == NULL) { return; } } if (meta) { meta_version = gctx->meta_version; } /* send metadata */ if (meta && meta_version != ctx->meta_version) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "gop cache send: meta"); if (handler->send_message_pt(s, meta, 0) == NGX_ERROR) { ngx_rtmp_finalize_session(s); return; } ctx->meta_version = meta_version; handler->free_message_pt(s, meta); } for (gf = cache->frame_head; gf; gf = gf->next) { csidx = !(lacf->interleave || gf->h.type == NGX_RTMP_MSG_VIDEO); cs = &ctx->cs[csidx]; lh = ch = gf->h; if (cs->active) { lh.timestamp = cs->timestamp; } delta = ch.timestamp - lh.timestamp; if (!cs->active) { switch (gf->h.type) { case NGX_RTMP_MSG_VIDEO: header = gctx->video_seq_header; break; default: header = gctx->audio_seq_header; } if (header) { apkt = handler->append_message_pt(s, &lh, NULL, header); if (apkt == NULL) { return; } } if (apkt && handler->send_message_pt(s, apkt, 0) != NGX_OK) { continue; } cs->timestamp = lh.timestamp; cs->active = 1; s->current_time = cs->timestamp; } pkt = handler->append_message_pt(s, &ch, &lh, gf->frame); if (pkt == NULL) { return; } if (handler->send_message_pt(s, pkt, gf->prio) != NGX_OK) { ++pub_ctx->ndropped; cs->dropped += delta; ngx_rtmp_finalize_session(s); return; } ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "gop cache send: tag type='%s' prio='%d' ctimestamp='%uD' " "ltimestamp='%uD'", gf->h.type == NGX_RTMP_MSG_AUDIO ? "audio" : "video", gf->prio, ch.timestamp, lh.timestamp); cs->timestamp += delta; s->current_time = cs->timestamp; if (pkt) { handler->free_message_pt(s, pkt); pkt = NULL; } if (apkt) { handler->free_message_pt(s, apkt); apkt = NULL; } } } }
static void ngx_rtmp_handshake_send(ngx_event_t *wev) { ngx_int_t n; ngx_connection_t *c; ngx_rtmp_session_t *s; ngx_buf_t *b; c = wev->data; s = c->hls ? c->hls_data : c->data; if (c->destroyed) { return; } if (wev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "handshake_send: send: client timed out, finish session."); c->timedout = 1; ngx_rtmp_finalize_session(s); return; } if (wev->timer_set) { ngx_del_timer(wev); } b = s->hs_buf; while(b->pos != b->last) { n = c->send(c, b->pos, b->last - b->pos); if (n == NGX_ERROR) { ngx_rtmp_finalize_session(s); ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "handshake_send: c->send: error"); return; } if (n == NGX_AGAIN || n == 0) { ngx_add_timer(c->write, s->timeout); if (ngx_handle_write_event(c->write, 0) != NGX_OK) { ngx_rtmp_finalize_session(s); ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "handshake_send: NGX_AGAIN "); } return; } b->pos += n; } if (wev->active) { ngx_del_event(wev, NGX_WRITE_EVENT, 0); } ++s->hs_stage; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "ngx_rtmp_handshake_send:handshake: stage %ui", s->hs_stage); switch (s->hs_stage) { case NGX_RTMP_HANDSHAKE_SERVER_SEND_RESPONSE: if (s->hs_old) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "handshake_send: old-style response"); s->hs_buf->pos = s->hs_buf->start + 1; s->hs_buf->last = s->hs_buf->end; } else if (ngx_rtmp_handshake_create_response(s) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "handshake_send: response error"); ngx_rtmp_finalize_session(s); return; } ngx_rtmp_handshake_send(wev); break; case NGX_RTMP_HANDSHAKE_SERVER_RECV_RESPONSE: s->hs_buf->pos = s->hs_buf->last = s->hs_buf->start + 1; ngx_rtmp_handshake_recv(c->read); break; case NGX_RTMP_HANDSHAKE_CLIENT_RECV_CHALLENGE: s->hs_buf->pos = s->hs_buf->last = s->hs_buf->start; ngx_rtmp_handshake_recv(c->read); break; case NGX_RTMP_HANDSHAKE_CLIENT_DONE: ngx_rtmp_handshake_done(s); break; } }
static ngx_int_t ngx_rtmp_live_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) { ngx_rtmp_session_t *ss; ngx_rtmp_live_ctx_t *ctx, **cctx, *pctx; //**rcctx;modify for warning ngx_rtmp_live_stream_t **stream; ngx_rtmp_live_app_conf_t *lacf; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL) { goto next; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL) { goto next; } if (ctx->stream == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: not joined"); goto next; } ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: leave '%s', publisher=%i, ctx->stream->publishing=%i", ctx->stream->name, ctx->publishing, ctx->stream->publishing); if (ctx->stream->publishing && ctx->publishing) { ctx->stream->publishing = 0; } for (cctx = &ctx->stream->ctx; *cctx; cctx = &(*cctx)->next) { if (*cctx == ctx) { *cctx = ctx->next; break; } } if (ctx->publishing || ctx->stream->active) { ngx_rtmp_live_stop(s); } if (ctx->publishing) { ngx_rtmp_send_status(s, "NetStream.Unpublish.Success", "status", "Stop publishing"); ctx->stream->bw_in.bandwidth = 0; ctx->stream->bw_real.bandwidth = 0; ctx->stream->bw_out.bandwidth = 0; if (ngx_rtmp_publishing > 0) { --ngx_rtmp_publishing; } if (!lacf->idle_streams) { for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) { if (pctx->publishing == 0) { ss = pctx->session; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, ss->connection->log, 0, "live: no publisher"); ngx_rtmp_finalize_session(ss); } } } else { ngx_uint_t nplayer = 0; for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) { if (pctx->publishing == 0) { if (ngx_memcmp(pctx->session->flashver.data, NGX_RTMP_RELAY_NAME, ngx_strlen(NGX_RTMP_RELAY_NAME)) == 0) { ss = pctx->session; ngx_log_error(NGX_LOG_INFO, ss->connection->log, 0, "live: close relay session"); ngx_rtmp_finalize_session(ss); } nplayer++; } } if (nplayer > 0) { ngx_rtmp_live_checking_publish(s, ctx->stream); } } }else { if (ctx->stream->ctx != NULL && ctx->stream->ctx->next == NULL && ctx->stream->ctx->publishing) { ngx_str_t strname; strname.data = ctx->stream->name; strname.len = ngx_strlen(ctx->stream->name); ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "live: last player close his connection."); ngx_rtmp_relay_player_dry(s, &strname); } if (ngx_rtmp_playing > 0) { --ngx_rtmp_playing; } } if (ctx->stream->ctx) { ctx->stream = NULL; goto next; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: delete empty stream '%s'", ctx->stream->name); ngx_del_timer(&(ctx->stream->check_evt)); stream = ngx_rtmp_live_get_stream(s, ctx->stream->name, 0); if (stream == NULL) { goto next; } *stream = (*stream)->next; ctx->stream->next = lacf->free_streams; lacf->free_streams = ctx->stream; ctx->stream = NULL; if (!ctx->silent && !ctx->publishing && !lacf->play_restart) { ngx_rtmp_send_status(s, "NetStream.Play.Stop", "status", "Stop live"); } next: return next_close_stream(s, v); }
static void ngx_rtmp_proxy_protocol_recv(ngx_event_t *rev) { u_char buf[107], *p, *pp, *text; size_t len; ssize_t n; ngx_err_t err; ngx_int_t i; ngx_addr_t addr; ngx_connection_t *c; ngx_rtmp_session_t *s; c = rev->data; s = c->data; if (c->destroyed) { return; } if (rev->timedout) { ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "proxy_protocol: recv: client timed out"); c->timedout = 1; ngx_rtmp_finalize_session(s); return; } if (rev->timer_set) { ngx_del_timer(rev); } n = recv(c->fd, (char *) buf, sizeof(buf), MSG_PEEK); err = ngx_socket_errno; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "recv(): %d", n); if (n == -1) { if (err == NGX_EAGAIN) { ngx_add_timer(rev, s->timeout); if (ngx_handle_read_event(c->read, 0) != NGX_OK) { ngx_rtmp_finalize_session(s); } return; } ngx_rtmp_finalize_session(s); return; } p = buf; if (n <= 8 && ngx_strncmp(p, "PROXY ", 6) != 0) { goto bad_header; } n -= 6; p += 6; ngx_memzero(&addr, sizeof(ngx_addr_t)); if (n >= 7 && ngx_strncmp(p, "UNKNOWN", 7) == 0) { n -= 7; p += 7; goto skip; } if (n < 5 || ngx_strncmp(p, "TCP", 3) != 0 || (p[3] != '4' && p[3] != '6') || p[4] != ' ') { goto bad_header; } n -= 5; p += 5; pp = ngx_strlchr(p, p + n, ' '); if (pp == NULL) { goto bad_header; } if (ngx_parse_addr(s->connection->pool, &addr, p, pp - p) != NGX_OK) { goto bad_header; } n -= pp - p; p = pp; skip: for (i = 0; i + 1 < n; i++) { if (p[i] == CR && p[i + 1] == LF) { break; } } if (i + 1 >= n) { goto bad_header; } n = p - buf + i + 2; if (c->recv(c, buf, n) != n) { goto failed; } if (addr.socklen) { text = ngx_palloc(s->connection->pool, NGX_SOCKADDR_STRLEN); if (text == NULL) { goto failed; } len = ngx_sock_ntop(addr.sockaddr, #if (nginx_version >= 1005003) addr.socklen, #endif text, NGX_SOCKADDR_STRLEN, 0); if (len == 0) { goto failed; } c->sockaddr = addr.sockaddr; c->socklen = addr.socklen; c->addr_text.data = text; c->addr_text.len = len; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "proxy_protocol: remote_addr:'%V'", &c->addr_text); } ngx_rtmp_handshake(s); return; bad_header: ngx_log_error(NGX_LOG_INFO, c->log, 0, "proxy_protocol: bad header"); failed: ngx_rtmp_finalize_session(s); }
/* 设置流状态 */ static void ngx_rtmp_live_set_status(ngx_rtmp_session_t *s, ngx_chain_t *control, ngx_chain_t **status, size_t nstatus, unsigned active) { ngx_rtmp_live_app_conf_t *lacf; ngx_rtmp_live_ctx_t *ctx, *pctx; ngx_chain_t **cl; ngx_event_t *e; size_t n; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: set active=%ui", active); if (ctx->active == active) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: unchanged active=%ui", active); return; } ctx->active = active; if (ctx->publishing) { /* publisher */ if (lacf->idle_timeout) { e = &ctx->idle_evt; if (active && !ctx->idle_evt.timer_set) { e->data = s->connection; e->log = s->connection->log; e->handler = ngx_rtmp_live_idle; ngx_add_timer(e, lacf->idle_timeout); } else if (!active && ctx->idle_evt.timer_set) { /* 需要启动流且启动空闲定时器,需要删除该定时器 */ ngx_del_timer(e); } } ctx->stream->active = active; for (pctx = ctx->stream->ctx; pctx; pctx = pctx->next) { /* 通知播放端 */ if (pctx->publishing == 0) { ngx_rtmp_live_set_status(pctx->session, control, status, nstatus, active); } } return; } /* subscriber */ if (control && ngx_rtmp_send_message(s, control, 0) != NGX_OK) { ngx_rtmp_finalize_session(s); return; } if (!ctx->silent) { cl = status; for (n = 0; n < nstatus; ++n, ++cl) { if (*cl && ngx_rtmp_send_message(s, *cl, 0) != NGX_OK) { ngx_rtmp_finalize_session(s); return; } } } ctx->cs[0].active = 0; ctx->cs[0].dropped = 0; ctx->cs[1].active = 0; ctx->cs[1].dropped = 0; }