static ngx_int_t ngx_rtmp_auto_push_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_auto_push_conf_t *apcf; ngx_rtmp_auto_push_ctx_t *ctx; if (s->auto_pushed || (s->relay && !s->static_relay)) { goto next; } apcf = (ngx_rtmp_auto_push_conf_t *) ngx_get_conf(ngx_cycle->conf_ctx, ngx_rtmp_auto_push_module); if (apcf->auto_push == 0) { goto next; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_auto_push_module); if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_auto_push_ctx_t)); if (ctx == NULL) { goto next; } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_auto_push_module); } ngx_memzero(ctx, sizeof(*ctx)); ctx->push_evt.data = s; ctx->push_evt.log = s->connection->log; ctx->push_evt.handler = ngx_rtmp_auto_push_reconnect; ctx->slots = ngx_pcalloc(s->connection->pool, sizeof(ngx_int_t) * NGX_MAX_PROCESSES); if (ctx->slots == NULL) { goto next; } ngx_memcpy(ctx->name, v->name, sizeof(ctx->name)); ngx_memcpy(ctx->args, v->args, sizeof(ctx->args)); ngx_rtmp_auto_push_reconnect(&ctx->push_evt); next: return next_publish(s, v); }
static ngx_int_t ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_rtmp_core_srv_conf_t *cscf; ngx_rtmp_codec_ctx_t *ctx; ngx_rtmp_frame_t **header; uint8_t fmt; u_char frametype; static ngx_uint_t sample_rates[] = { 5512, 11025, 22050, 44100 }; if (h->type != NGX_RTMP_MSG_AUDIO && h->type != NGX_RTMP_MSG_VIDEO) { return NGX_OK; } if (h->type == NGX_RTMP_MSG_VIDEO) { frametype = in->buf->pos[0] & 0xf0; if (frametype != 0x10 && frametype != 0x20) { ngx_log_error(NGX_LOG_ERR, s->log, 0, "codec: receive unkwnon frametype %02xD", frametype); return NGX_OK; } } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->pool, sizeof(ngx_rtmp_codec_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_codec_module); } /* save codec */ if (in->buf->last - in->buf->pos < 1) { return NGX_OK; } fmt = in->buf->pos[0]; if (h->type == NGX_RTMP_MSG_AUDIO) { ctx->audio_codec_id = (fmt & 0xf0) >> 4; ctx->audio_channels = (fmt & 0x01) + 1; ctx->sample_size = (fmt & 0x02) ? 2 : 1; if (ctx->sample_rate == 0) { ctx->sample_rate = sample_rates[(fmt & 0x0c) >> 2]; }
static ngx_int_t ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_rtmp_core_srv_conf_t *cscf; ngx_rtmp_codec_ctx_t *ctx; ngx_chain_t **header, **pheader; uint8_t fmt; ngx_rtmp_header_t ch, lh; ngx_uint_t *version, idx; u_char *p; static ngx_uint_t sample_rates[] = { 5512, 11025, 22050, 44100 }; static ngx_uint_t aac_sample_rates[] = { 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 }; if (h->type != NGX_RTMP_MSG_AUDIO && h->type != NGX_RTMP_MSG_VIDEO) { return NGX_OK; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_codec_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_codec_module); } /* save codec */ if (in->buf->last - in->buf->pos < 1) { return NGX_OK; } fmt = in->buf->pos[0]; if (h->type == NGX_RTMP_MSG_AUDIO) { ctx->audio_codec_id = (fmt & 0xf0) >> 4; ctx->audio_channels = (fmt & 0x01) + 1; ctx->sample_size = (fmt & 0x02) ? 2 : 1; if (ctx->aac_sample_rate == 0) { ctx->sample_rate = sample_rates[(fmt & 0x0c) >> 2]; }
static ngx_rtmp_log_ctx_t * ngx_rtmp_log_set_names(ngx_rtmp_session_t *s, u_char *name, u_char *args) { ngx_rtmp_log_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_log_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_log_ctx_t)); if (ctx == NULL) { return NULL; } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_log_module); } ngx_memcpy(ctx->name, name, NGX_RTMP_MAX_NAME); ngx_memcpy(ctx->args, args, NGX_RTMP_MAX_ARGS); return ctx; }
static ngx_int_t ngx_rtmp_flv_init(ngx_rtmp_session_t *s, ngx_file_t *f) { ngx_rtmp_flv_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_flv_module); if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_flv_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_flv_module); } ngx_memzero(ctx, sizeof(*ctx)); return NGX_OK; }
static ngx_int_t ngx_rtmp_exec_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_exec_app_conf_t *eacf; ngx_rtmp_exec_conf_t *ec; ngx_rtmp_exec_ctx_t *ctx; size_t n; eacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_exec_module); if (eacf == NULL || eacf->execs.nelts == 0) { goto next; } if (s->auto_pushed) { goto next; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_exec_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_exec_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_exec_module); ctx->execs = ngx_pcalloc(s->connection->pool, eacf->execs.nelts * sizeof(ngx_rtmp_exec_t)); } ngx_memcpy(ctx->name, v->name, NGX_RTMP_MAX_NAME); ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "exec: run %uz command(s)", eacf->execs.nelts); ec = eacf->execs.elts; for (n = 0; n < eacf->execs.nelts; ++n, ++ec) { ngx_rtmp_exec_run(s, n); } next: return next_publish(s, v); }
static ngx_rtmp_log_ctx_t * ngx_rtmp_log_set_names(ngx_rtmp_session_t *s, u_char *name, u_char *args) { ngx_rtmp_log_ctx_t *ctx; ngx_rtmp_log_app_conf_t *lacf; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_log_module); if (lacf == NULL || lacf->off || lacf->logs == NULL) { return NULL; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_log_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_log_ctx_t)); if (ctx == NULL) { return NULL; } if (lacf->interval) { ctx->ev.handler = ngx_rtmp_log_split_output_handler; ctx->ev.log = s->connection->log; ctx->ev.data = s; ctx->ev.timer_set = 0; ctx->last_sent = 0; ctx->last_received = 0; ngx_add_timer(&ctx->ev, lacf->interval); } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_log_module); } ngx_memcpy(ctx->name, name, NGX_RTMP_MAX_NAME); ngx_memcpy(ctx->args, args, NGX_RTMP_MAX_ARGS); return ctx; }
static ngx_int_t ngx_rtmp_gop_cache_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_gop_cache_app_conf_t *gacf; ngx_rtmp_gop_cache_ctx_t *ctx; gacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_gop_cache_module); if (gacf == NULL || !gacf->gop_cache) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "gop cache publish: name='%s' type='%s'", v->name, v->type); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_gop_cache_module); if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_gop_cache_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_gop_cache_module); } ngx_memzero(ctx, sizeof(*ctx)); if (ctx->pool == NULL) { ctx->pool = ngx_create_pool(NGX_GOP_CACHE_POOL_CREATE_SIZE, s->connection->log); if (ctx->pool == NULL) { return NGX_ERROR; } } next: return next_publish(s, v); }
static ngx_int_t ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { u_char *p; size_t len; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_dash_frag_t *f; ngx_rtmp_dash_app_conf_t *dacf; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { goto next; } if (s->auto_pushed) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash: publish: name='%s' type='%s'", v->name, v->type); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_dash_ctx_t)); if (ctx == NULL) { goto next; } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_dash_module); } else { if (ctx->opened) { goto next; } f = ctx->frags; ngx_memzero(ctx, sizeof(ngx_rtmp_dash_ctx_t)); ctx->frags = f; } if (ctx->frags == NULL) { ctx->frags = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_dash_frag_t) * (dacf->winfrags * 2 + 1)); if (ctx->frags == NULL) { return NGX_ERROR; } } ctx->id = 0; if (ngx_strstr(v->name, "..")) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: bad stream name: '%s'", v->name); return NGX_ERROR; } ctx->name.len = ngx_strlen(v->name); ctx->name.data = ngx_palloc(s->connection->pool, ctx->name.len + 1); if (ctx->name.data == NULL) { return NGX_ERROR; } *ngx_cpymem(ctx->name.data, v->name, ctx->name.len) = 0; len = dacf->path.len + 1 + ctx->name.len + sizeof(".mpd"); if (dacf->nested) { len += sizeof("/index") - 1; } ctx->playlist.data = ngx_palloc(s->connection->pool, len); p = ngx_cpymem(ctx->playlist.data, dacf->path.data, dacf->path.len); if (p[-1] != '/') { *p++ = '/'; } p = ngx_cpymem(p, ctx->name.data, ctx->name.len); /* * ctx->stream holds initial part of stream file path * however the space for the whole stream path * is allocated */ ctx->stream.len = p - ctx->playlist.data + 1; ctx->stream.data = ngx_palloc(s->connection->pool, ctx->stream.len + NGX_INT32_LEN + sizeof(".m4x")); ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1); ctx->stream.data[ctx->stream.len - 1] = (dacf->nested ? '/' : '-'); if (dacf->nested) { p = ngx_cpymem(p, "/index.mpd", sizeof("/index.mpd") - 1); } else { p = ngx_cpymem(p, ".mpd", sizeof(".mpd") - 1); } ctx->playlist.len = p - ctx->playlist.data; *p = 0; /* playlist bak (new playlist) path */ ctx->playlist_bak.data = ngx_palloc(s->connection->pool, ctx->playlist.len + sizeof(".bak")); p = ngx_cpymem(ctx->playlist_bak.data, ctx->playlist.data, ctx->playlist.len); p = ngx_cpymem(p, ".bak", sizeof(".bak") - 1); ctx->playlist_bak.len = p - ctx->playlist_bak.data; *p = 0; ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash: playlist='%V' playlist_bak='%V' stream_pattern='%V'", &ctx->playlist, &ctx->playlist_bak, &ctx->stream); ctx->start_time = *ngx_cached_time; if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) { return NGX_ERROR; } next: return next_publish(s, v); }
static ngx_int_t ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_play_main_conf_t *pmcf; ngx_rtmp_play_app_conf_t *pacf; ngx_rtmp_play_ctx_t *ctx; u_char *p; ngx_rtmp_play_fmt_t *fmt, **pfmt; ngx_str_t *pfx, *sfx; ngx_str_t name; ngx_uint_t n; pmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_play_module); pacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_play_module); if (pacf == NULL || pacf->entries.nelts == 0) { goto next; } ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "play: play name='%s' timestamp=%i", v->name, (ngx_int_t) v->start); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx && ctx->file.fd != NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: already playing"); goto next; } /* check for double-dot in v->name; * we should not move out of play directory */ for (p = v->name; *p; ++p) { if (ngx_path_separator(p[0]) && p[1] == '.' && p[2] == '.' && ngx_path_separator(p[3])) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: bad name '%s'", v->name); return NGX_ERROR; } } if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_play_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_play_module); } ngx_memzero(ctx, sizeof(*ctx)); ctx->session = s; ctx->aindex = ngx_rtmp_play_parse_index('a', v->args); ctx->vindex = ngx_rtmp_play_parse_index('v', v->args); ctx->file.log = s->connection->log; ngx_memcpy(ctx->name, v->name, NGX_RTMP_MAX_NAME); name.len = ngx_strlen(v->name); name.data = v->name; pfmt = pmcf->fmts.elts; for (n = 0; n < pmcf->fmts.nelts; ++n, ++pfmt) { fmt = *pfmt; pfx = &fmt->pfx; sfx = &fmt->sfx; if (pfx->len == 0 && ctx->fmt == NULL) { ctx->fmt = fmt; } if (pfx->len && name.len >= pfx->len && ngx_strncasecmp(pfx->data, name.data, pfx->len) == 0) { ctx->pfx_size = pfx->len; ctx->fmt = fmt; break; } if (name.len >= sfx->len && ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len, sfx->len) == 0) { ctx->fmt = fmt; } } if (ctx->fmt == NULL) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: fmt not found"); goto next; } ctx->file.fd = NGX_INVALID_FILE; ctx->nentry = NGX_CONF_UNSET_UINT; ctx->post_seek = NGX_CONF_UNSET_UINT; sfx = &ctx->fmt->sfx; if (name.len < sfx->len || ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len, sfx->len)) { ctx->sfx = *sfx; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: fmt=%V", &ctx->fmt->name); return ngx_rtmp_play_next_entry(s, v); next: return next_play(s, v); }
ngx_int_t ngx_rtmp_netcall_create(ngx_rtmp_session_t *s, ngx_rtmp_netcall_init_t *ci) { ngx_rtmp_netcall_ctx_t *ctx; ngx_peer_connection_t *pc; ngx_rtmp_netcall_session_t *cs; ngx_rtmp_netcall_app_conf_t *cacf; ngx_connection_t *c, *cc; ngx_pool_t *pool; ngx_int_t rc; pool = NULL; c = s->connection; cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_netcall_module); if (cacf == NULL) { goto error; } /* get module context */ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_netcall_module); if (ctx == NULL) { ctx = ngx_pcalloc(c->pool, sizeof(ngx_rtmp_netcall_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_netcall_module); } /* Create netcall pool, connection, session. * Note we use shared (app-wide) log because * s->connection->log might be unavailable * in detached netcall when it's being closed */ pool = ngx_create_pool(4096, cacf->log); if (pool == NULL) { goto error; } pc = ngx_pcalloc(pool, sizeof(ngx_peer_connection_t)); if (pc == NULL) { goto error; } cs = ngx_pcalloc(pool, sizeof(ngx_rtmp_netcall_session_t)); if (cs == NULL) { goto error; } /* copy arg to connection pool */ if (ci->argsize) { cs->arg = ngx_pcalloc(pool, ci->argsize); if (cs->arg == NULL) { goto error; } ngx_memcpy(cs->arg, ci->arg, ci->argsize); } cs->timeout = cacf->timeout; cs->bufsize = cacf->bufsize; cs->url = ci->url; cs->session = s; cs->filter = ci->filter; cs->sink = ci->sink; cs->handle = ci->handle; if (cs->handle == NULL) { cs->detached = 1; } pc->log = cacf->log; pc->get = ngx_rtmp_netcall_get_peer; pc->free = ngx_rtmp_netcall_free_peer; pc->data = cs; /* connect */ rc = ngx_event_connect_peer(pc); if (rc != NGX_OK && rc != NGX_AGAIN ) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "netcall: connection failed"); goto error; } cc = pc->connection; cc->data = cs; cc->pool = pool; cs->pc = pc; cs->out = ci->create(s, ci->arg, pool); if (cs->out == NULL) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "netcall: creation failed"); ngx_close_connection(pc->connection); goto error; } cc->write->handler = ngx_rtmp_netcall_send; cc->read->handler = ngx_rtmp_netcall_recv; if (!cs->detached) { cs->next = ctx->cs; ctx->cs = cs; } ngx_rtmp_netcall_send(cc->write); return c->destroyed ? NGX_ERROR : NGX_OK; error: if (pool) { ngx_destroy_pool(pool); } return NGX_ERROR; }
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; }
/* 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_simple_relay(ngx_rtmp_session_t *s, ngx_live_relay_t *relay, unsigned publishing) { ngx_rtmp_session_t *rs; ngx_live_relay_ctx_t *ctx, *pctx; ngx_live_relay_app_conf_t *lracf; ngx_live_relay_simple_ctx_t *sctx; rs = ngx_rtmp_create_relay_session(s, &ngx_live_relay_simple_module); if (rs == NULL) { ngx_log_error(NGX_LOG_ERR, s->log, 0, "relay simple, create relay session failed"); return NGX_DECLINED; } rs->publishing = publishing; rs->live_stream = s->live_stream; ngx_live_create_ctx(rs, publishing); sctx = ngx_pcalloc(rs->pool, sizeof(ngx_live_relay_simple_ctx_t)); if (sctx == NULL) { ngx_log_error(NGX_LOG_ERR, rs->log, 0, "relay simple, create simple relay ctx failed"); ngx_rtmp_finalize_session(rs); return NGX_OK; } ngx_rtmp_set_ctx(rs, sctx, ngx_live_relay_simple_module); sctx->relay = relay; 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_simple_handler; if (s->publishing != rs->publishing) { ngx_post_event(&ctx->reconnect, &ngx_posted_events); return NGX_OK; } // normal publisher close, need to trigger pull if (s->publishing && !s->relay) { 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; }
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; // ngx_rtmp_session_t *ss; // ngx_rtmp_live_ctx_t *pctx; 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_error(NGX_LOG_INFO, s->connection->log, 0, "live: already joined '%s'", name); 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_error(NGX_LOG_INFO, s->connection->log, 0, "live: join '%s'", name); // 获取该name对应的stream,通常在publish的时候创建,在播放的时候直接获取就即可 stream = ngx_rtmp_live_get_stream(s, name, publisher || lacf->idle_streams); // 播放端 在拉流的时候,流还没有建立,则结束session if (stream == NULL || !(publisher || (*stream)->publishing || lacf->idle_streams)) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "live: name = %s stream not found", name); ngx_rtmp_send_status(s, "NetStream.Play.StreamNotFound", "error", "No such stream"); ngx_rtmp_finalize_session(s); return; } // 上传端在推流的时候,发现流名称已经被占用,则发送status消息 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; } //这个stream对应的是publish时创建的流 ctx->stream = *stream; ctx->publishing = publisher; // 下面两个操作是将新创建的ctx插入stream的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; // 作为一个subscriber,会执行 if (!ctx->publishing && ctx->stream->active) { ngx_rtmp_live_start(s); } }
static ngx_int_t ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_play_main_conf_t *pmcf; ngx_rtmp_play_app_conf_t *pacf; ngx_rtmp_play_ctx_t *ctx; u_char *p; ngx_event_t *e; ngx_rtmp_play_fmt_t *fmt, **pfmt; ngx_str_t *pfx, *sfx; ngx_str_t name; ngx_uint_t n; static ngx_str_t nosfx; static u_char path[NGX_MAX_PATH]; pmcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_play_module); pacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_play_module); if (pacf == NULL || pacf->root.len == 0) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: play name='%s' timestamp=%i", v->name, (ngx_int_t) v->start); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx && ctx->file.fd != NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: already playing"); goto next; } /* check for double-dot in v->name; * we should not move out of play directory */ for (p = v->name; *p; ++p) { if (ngx_path_separator(p[0]) && p[1] == '.' && p[2] == '.' && ngx_path_separator(p[3])) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: bad name '%s'", v->name); return NGX_ERROR; } } if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_play_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_play_module); } ngx_memzero(ctx, sizeof(*ctx)); ctx->file.log = s->connection->log; name.len = ngx_strlen(v->name); name.data = v->name; pfmt = pmcf->fmts.elts; for (n = 0; n < pmcf->fmts.nelts; ++n, ++pfmt) { fmt = *pfmt; pfx = &fmt->pfx; sfx = &fmt->sfx; if (pfx->len == 0 && ctx->fmt == NULL) { ctx->fmt = fmt; } if (pfx->len && name.len >= pfx->len && ngx_strncasecmp(pfx->data, name.data, pfx->len) == 0) { name.data += pfx->len; name.len -= pfx->len; ctx->fmt = fmt; break; } if (name.len >= sfx->len && ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len, sfx->len) == 0) { ctx->fmt = fmt; } } if (ctx->fmt == NULL) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: fmt not found"); goto next; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: fmt found: '%V'", &ctx->fmt->name); sfx = &ctx->fmt->sfx; if (name.len >= sfx->len && ngx_strncasecmp(sfx->data, name.data + name.len - sfx->len, sfx->len) == 0) { sfx = &nosfx; } p = ngx_snprintf(path, sizeof(path), "%V/%V%V", &pacf->root, &name, sfx); *p = 0; ctx->file.fd = ngx_open_file(path, NGX_FILE_RDONLY, NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); if (ctx->file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: error opening file '%s'", path); return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: opened file '%s'", path); e = &ctx->send_evt; e->data = s; e->handler = ngx_rtmp_play_send; e->log = s->connection->log; ngx_rtmp_send_user_recorded(s, 1); if (ngx_rtmp_play_init(s) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_play_start(s, v->start) != NGX_OK) { return NGX_ERROR; } next: return next_play(s, v); }
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); stream = ngx_rtmp_live_get_stream(s, name, publisher || lacf->idle_streams || s->relay_type != NGX_NONE_RELAY); if (stream == NULL || !(publisher || (*stream)->publishing || lacf->idle_streams || s->relay_type != NGX_NONE_RELAY )) { 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: '%V/%s', already publishing", &s->app, name); ngx_rtmp_send_status(s, "NetStream.Publish.BadName", "error", "Already publishing"); return; } (*stream)->publishing = 1; //del checking timer ngx_del_timer(&(*stream)->check_evt); } else { ngx_str_t strname; strname.data = name; strname.len = ngx_strlen(name); ngx_rtmp_relay_player_new(s, &strname); } ctx->stream = *stream; ctx->publishing = publisher; ctx->next = (*stream)->ctx; ctx->hls = s->hls; ngx_memcpy(s->name, name, ngx_strlen(name)); s->name[ngx_strlen(name)] = 0; (*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 && !ctx->hls) { ngx_rtmp_live_start(s); } if (!publisher && !(*stream)->publishing) { ngx_str_t strname; strname.data = name; strname.len = ngx_strlen(name); if (ngx_rtmp_relay_relaying(s, &strname) == NGX_ERROR) { ngx_rtmp_live_checking_publish(s, *stream); } } }
static ngx_int_t ngx_rtmp_exec_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_exec_main_conf_t *emcf; ngx_rtmp_exec_app_conf_t *eacf; ngx_rtmp_exec_t *e; ngx_rtmp_exec_conf_t *ec; ngx_rtmp_exec_ctx_t *ctx; size_t n; emcf = ngx_rtmp_get_module_main_conf(s, ngx_rtmp_exec_module); eacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_exec_module); if (eacf == NULL || eacf->confs.nelts == 0) { goto next; } if (s->auto_pushed) { goto next; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_exec_module); if (ctx == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_exec_ctx_t)); if (ctx == NULL) { return NGX_ERROR; } ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_exec_module); if (ngx_array_init(&ctx->execs, s->connection->pool, eacf->confs.nelts, sizeof(ngx_rtmp_exec_t)) != NGX_OK) { return NGX_ERROR; } e = ngx_array_push_n(&ctx->execs, eacf->confs.nelts); if (e == NULL) { return NGX_ERROR; } ec = eacf->confs.elts; for (n = 0; n < eacf->confs.nelts; ++n, ++e, ++ec) { ngx_memzero(e, sizeof(*e)); e->conf = ec; e->log = s->connection->log; e->session = s; e->kill_signal = emcf->kill_signal; e->respawn_timeout = (eacf->respawn ? emcf->respawn_timeout : NGX_CONF_UNSET_MSEC); } } ngx_memcpy(ctx->name, v->name, NGX_RTMP_MAX_NAME); ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "exec: run %uz command(s)", ctx->execs.nelts); e = ctx->execs.elts; for (n = 0; n < ctx->execs.nelts; ++n, ++e) { ngx_rtmp_exec_run(e); } next: return next_publish(s, v); }
static ngx_int_t ngx_rtmp_play_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_play_app_conf_t *pacf; ngx_rtmp_play_ctx_t *ctx; u_char *p; ngx_event_t *e; size_t len, slen; static u_char path[NGX_MAX_PATH]; pacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_play_module); if (pacf == NULL || pacf->root.len == 0) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: play name='%s' timestamp=%i", v->name, (ngx_int_t) v->start); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx && ctx->file.fd != NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: already playing"); goto next; } /* check for double-dot in v->name; * we should not move out of play directory */ p = v->name; while (*p) { if (*p == '.' && *(p + 1) == '.') { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: bad name '%s'", v->name); return NGX_ERROR; } ++p; } if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_play_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_play_module); } ngx_memzero(ctx, sizeof(*ctx)); ctx->file.log = s->connection->log; /* make file path */ len = ngx_strlen(v->name); slen = sizeof(".flv") - 1; p = ngx_snprintf(path, sizeof(path), "%V/%s%s", &pacf->root, v->name, len > slen && ngx_strncasecmp((u_char *) ".flv", v->name + len - slen, slen) == 0 ? "" : ".flv"); *p = 0; /* open file */ ctx->file.fd = ngx_open_file(path, NGX_FILE_RDONLY, NGX_FILE_OPEN, NGX_FILE_DEFAULT_ACCESS); if (ctx->file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: error opening file %s", path); goto next; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: opened file '%s'", path); e = &ctx->write_evt; e->data = s; e->handler = ngx_rtmp_play_send; e->log = s->connection->log; ngx_rtmp_send_user_recorded(s, 1); ngx_rtmp_play_start(s, v->start); next: return next_play(s, v); }
static ngx_rtmp_relay_ctx_t * ngx_rtmp_relay_create_remote_ctx(ngx_rtmp_session_t *s, ngx_str_t* name, ngx_rtmp_relay_target_t *target) { ngx_rtmp_relay_ctx_t *rctx; ngx_rtmp_addr_conf_t *addr_conf; ngx_rtmp_conf_ctx_t *addr_ctx; ngx_rtmp_session_t *rs; ngx_rtmp_relay_app_conf_t *racf; ngx_peer_connection_t *pc; ngx_connection_t *c; ngx_pool_t *pool; ngx_int_t rc; ngx_str_t v, *uri; u_char *first, *last, *p; racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_relay_module); pool = NULL; pool = ngx_create_pool(4096, racf->log); if (pool == NULL) { return NULL; } rctx = ngx_pcalloc(pool, sizeof(ngx_rtmp_relay_ctx_t)); if (rctx == NULL) { goto clear; } if (ngx_rtmp_relay_copy_str(pool, &rctx->name, name) != NGX_OK || ngx_rtmp_relay_copy_str(pool, &rctx->url, &target->url.url) != NGX_OK) { goto clear; } rctx->tag = target->tag; rctx->data = target->data; #define NGX_RTMP_RELAY_STR_COPY(to, from) \ if (ngx_rtmp_relay_copy_str(pool, &rctx->to, &target->from) != NGX_OK) { \ goto clear; \ } NGX_RTMP_RELAY_STR_COPY(app, app); NGX_RTMP_RELAY_STR_COPY(tc_url, tc_url); NGX_RTMP_RELAY_STR_COPY(page_url, page_url); NGX_RTMP_RELAY_STR_COPY(swf_url, swf_url); NGX_RTMP_RELAY_STR_COPY(flash_ver, flash_ver); NGX_RTMP_RELAY_STR_COPY(play_path, play_path); rctx->live = target->live; rctx->start = target->start; rctx->stop = target->stop; #undef NGX_RTMP_RELAY_STR_COPY if (rctx->app.len == 0 || rctx->play_path.len == 0) { /* parse uri */ uri = &target->url.uri; first = uri->data; last = uri->data + uri->len; if (first != last && *first == '/') { ++first; } if (first != last) { /* deduce app */ p = ngx_strlchr(first, last, '/'); if (rctx->app.len == 0 && first != p) { v.data = first; v.len = p - first; if (ngx_rtmp_relay_copy_str(pool, &rctx->app, &v) != NGX_OK) { goto clear; } } /* deduce play_path */ ++p; if (rctx->play_path.len == 0 && p != last) { v.data = p; v.len = last - p; if (ngx_rtmp_relay_copy_str(pool, &rctx->play_path, &v) != NGX_OK) { goto clear; } } } } rctx->relay = 1; pc = ngx_pcalloc(pool, sizeof(ngx_peer_connection_t)); if (pc == NULL) { goto clear; } /* copy log to keep shared log unchanged */ rctx->log = *racf->log; pc->log = &rctx->log; pc->get = ngx_rtmp_relay_get_peer; pc->free = ngx_rtmp_relay_free_peer; pc->name = &target->url.host; pc->socklen = target->url.socklen; pc->sockaddr = (struct sockaddr *)ngx_palloc(pool, pc->socklen); if (pc->sockaddr == NULL) { goto clear; } ngx_memcpy(pc->sockaddr, &target->url.sockaddr, pc->socklen); rc = ngx_event_connect_peer(pc); if (rc != NGX_OK && rc != NGX_AGAIN ) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, racf->log, 0, "relay: connection failed"); goto clear; } c = pc->connection; c->pool = pool; c->addr_text = rctx->url; addr_conf = ngx_pcalloc(pool, sizeof(ngx_rtmp_addr_conf_t)); if (addr_conf == NULL) { goto clear; } addr_ctx = ngx_pcalloc(pool, sizeof(ngx_rtmp_conf_ctx_t)); if (addr_ctx == NULL) { goto clear; } addr_conf->ctx = addr_ctx; addr_ctx->main_conf = s->main_conf; addr_ctx->srv_conf = s->srv_conf; ngx_str_set(&addr_conf->addr_text, "ngx-relay"); rs = ngx_rtmp_init_session(c, addr_conf); if (rs == NULL) { /* no need to destroy pool */ return NULL; } rs->app_conf = s->app_conf; rctx->session = rs; ngx_rtmp_set_ctx(rs, rctx, ngx_rtmp_relay_module); ngx_str_set(&rs->flashver, "ngx-local-relay"); ngx_rtmp_client_handshake(rs, 1); return rctx; clear: if (pool) { ngx_destroy_pool(pool); } return NULL; }
static ngx_int_t ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_hls_app_conf_t *hacf; ngx_rtmp_hls_ctx_t *ctx; size_t len; u_char *p; hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module); if (hacf == NULL || !hacf->hls || hacf->path.len == 0) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "hls: publish: name='%s' type='%s'", v->name, v->type); /* create context */ ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); if (ctx == NULL) { ctx = ngx_palloc(s->connection->pool, sizeof(ngx_rtmp_hls_ctx_t)); ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_hls_module); } ngx_memzero(ctx, sizeof(ngx_rtmp_hls_ctx_t)); /* init names & paths */ len = ngx_strlen(v->name); ctx->name.len = len + (ngx_uint_t)ngx_escape_uri(NULL, v->name, len, NGX_ESCAPE_URI_COMPONENT); ctx->name.data = ngx_palloc(s->connection->pool, ctx->name.len); ngx_escape_uri(ctx->name.data, v->name, len, NGX_ESCAPE_URI_COMPONENT); ctx->playlist.data = ngx_palloc(s->connection->pool, hacf->path.len + 1 + ctx->name.len + sizeof(".m3u8")); p = ngx_cpymem(ctx->playlist.data, hacf->path.data, hacf->path.len); if (p[-1] != '/') { *p++ = '/'; } p = ngx_cpymem(p, ctx->name.data, ctx->name.len); /* ctx->stream_path holds initial part of stream file path * however the space for the whole stream path * is allocated */ ctx->stream.len = p - ctx->playlist.data; ctx->stream.data = ngx_palloc(s->connection->pool, ctx->stream.len + 1 + NGX_OFF_T_LEN + sizeof(".ts")); ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len); /* playlist path */ p = ngx_cpymem(p, ".m3u8", sizeof(".m3u8") - 1); ctx->playlist.len = p - ctx->playlist.data; *p = 0; /* playlist bak (new playlist) path */ ctx->playlist_bak.data = ngx_palloc(s->connection->pool, ctx->playlist.len + sizeof(".bak")); p = ngx_cpymem(ctx->playlist_bak.data, ctx->playlist.data, ctx->playlist.len); p = ngx_cpymem(p, ".bak", sizeof(".bak") - 1); ctx->playlist_bak.len = p - ctx->playlist_bak.data; *p = 0; ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "hls: playlist='%V' playlist_bak='%V' stream_pattern='%V'", &ctx->playlist, &ctx->playlist_bak, &ctx->stream); /* schedule restart event */ ctx->publishing = 1; ctx->frag_start = ngx_current_msec - hacf->fraglen - 1; next: return next_publish(s, v); }