static ngx_int_t ngx_rtmp_play_seek(ngx_rtmp_session_t *s, ngx_rtmp_seek_t *v) { ngx_rtmp_play_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx == NULL || ctx->file.fd == NGX_INVALID_FILE) { goto next; } if (ngx_rtmp_send_stream_eof(s, NGX_RTMP_MSID) != NGX_OK) { return NGX_ERROR; } ngx_rtmp_play_do_seek(s, v->offset); if (ngx_rtmp_send_status(s, "NetStream.Seek.Notify", "status", "Seeking") != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_send_stream_begin(s, NGX_RTMP_MSID) != NGX_OK) { return NGX_ERROR; } next: return next_seek(s, v); }
static ngx_int_t ngx_rtmp_live_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_live_app_conf_t *lacf; ngx_rtmp_live_ctx_t *ctx; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL || !lacf->live) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: publish: name='%s' type='%s'", v->name, v->type); /* join stream as publisher */ ngx_rtmp_live_join(s, v->name, 1); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL || !ctx->publishing) { goto next; } ctx->silent = v->silent; if (!ctx->silent) { ngx_rtmp_send_status(s, "NetStream.Publish.Start", "status", "Start publishing"); } next: return next_publish(s, v); }
static ngx_int_t ngx_rtmp_play_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) { ngx_rtmp_play_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx == NULL) { goto next; } ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: close_stream"); ngx_rtmp_play_do_stop(s); ngx_rtmp_play_do_done(s); if (ctx->file.fd != NGX_INVALID_FILE) { ngx_close_file(ctx->file.fd); ctx->file.fd = NGX_INVALID_FILE; ngx_rtmp_send_stream_eof(s, NGX_RTMP_MSID); ngx_rtmp_send_status(s, "NetStream.Play.Stop", "status", "Stop video on demand"); } if (ctx->file_id) { ngx_rtmp_play_cleanup_local_file(s); } next: return next_close_stream(s, v); }
static ngx_int_t ngx_rtmp_play_open(ngx_rtmp_session_t *s, double start) { ngx_rtmp_play_ctx_t *ctx; ngx_event_t *e; ngx_uint_t timestamp; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx->file.fd == NGX_INVALID_FILE) { return NGX_ERROR; } if (ngx_rtmp_send_stream_begin(s, NGX_RTMP_MSID) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_send_status(s, "NetStream.Play.Start", "status", "Start video on demand") != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_play_join(s) != NGX_OK) { return NGX_ERROR; } e = &ctx->send_evt; e->data = s; e->handler = ngx_rtmp_play_send; e->log = s->connection->log; ngx_rtmp_send_recorded(s, 1); if (ngx_rtmp_send_sample_access(s) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_play_do_init(s) != NGX_OK) { return NGX_ERROR; } timestamp = ctx->post_seek != NGX_CONF_UNSET_UINT ? ctx->post_seek : (start < 0 ? 0 : (ngx_uint_t) start); if (ngx_rtmp_play_do_seek(s, timestamp) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_play_do_start(s) != NGX_OK) { return NGX_ERROR; } ctx->opened = 1; return NGX_OK; }
static ngx_int_t ngx_rtmp_play_pause(ngx_rtmp_session_t *s, ngx_rtmp_pause_t *v) { ngx_rtmp_play_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx == NULL || ctx->file.fd == NGX_INVALID_FILE) { goto next; } if (!ctx->opened) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: pause ignored"); goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: pause=%i timestamp=%f", (ngx_int_t) v->pause, v->position); if (v->pause) { if (ngx_rtmp_send_status(s, "NetStream.Pause.Notify", "status", "Paused video on demand") != NGX_OK) { return NGX_ERROR; } ngx_rtmp_play_do_stop(s); } else { if (ngx_rtmp_send_status(s, "NetStream.Unpause.Notify", "status", "Unpaused video on demand") != NGX_OK) { return NGX_ERROR; } ngx_rtmp_play_do_start(s); /*TODO: v->position? */ } next: return next_pause(s, v); }
/* 客户端通知服务端停止或启动播放 */ static ngx_int_t ngx_rtmp_live_pause(ngx_rtmp_session_t *s, ngx_rtmp_pause_t *v) { ngx_rtmp_live_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL || ctx->stream == NULL) { goto next; } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: pause=%i timestamp=%f", (ngx_int_t) v->pause, v->position); if (v->pause) { if (ngx_rtmp_send_status(s, "NetStream.Pause.Notify", "status", "Paused live") != NGX_OK) { return NGX_ERROR; } ctx->paused = 1; ngx_rtmp_live_stop(s); } else { if (ngx_rtmp_send_status(s, "NetStream.Unpause.Notify", "status", "Unpaused live") != NGX_OK) { return NGX_ERROR; } ctx->paused = 0; ngx_rtmp_live_start(s); } next: return next_pause(s, v); }
static void ngx_rtmp_play_send(ngx_event_t *e) { ngx_rtmp_session_t *s = e->data; ngx_rtmp_play_ctx_t *ctx; ngx_int_t rc; ngx_uint_t ts; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx == NULL || ctx->fmt == NULL || ctx->fmt->send == NULL) { return; } ts = 0; rc = ctx->fmt->send(s, &ctx->file, &ts); if (rc > 0) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: send schedule %i", rc); ngx_add_timer(e, rc); return; } if (rc == NGX_AGAIN) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: send buffer full"); ngx_post_event(e, &s->posted_dry_events); return; } if (rc == NGX_OK) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: send restart"); ngx_post_event(e, &ngx_posted_events); return; } ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: send done"); ngx_rtmp_send_stream_eof(s, NGX_RTMP_MSID); ngx_rtmp_send_play_status(s, "NetStream.Play.Complete", "status", ts, 0); ngx_rtmp_send_status(s, "NetStream.Play.Stop", "status", "Stopped"); }
static ngx_int_t ngx_rtmp_record_node_close(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx) { ngx_rtmp_record_app_conf_t *rracf; ngx_err_t err; void **app_conf; ngx_int_t rc; ngx_rtmp_record_done_t v; rracf = rctx->conf; if (rctx->file.fd == NGX_INVALID_FILE) { return NGX_AGAIN; } if (ngx_close_file(rctx->file.fd) == NGX_FILE_ERROR) { err = ngx_errno; ngx_log_error(NGX_LOG_CRIT, s->connection->log, err, "record: %V error closing file", &rracf->id); ngx_rtmp_record_notify_error(s, rctx); } rctx->file.fd = NGX_INVALID_FILE; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V closed", &rracf->id); if (rracf->notify) { ngx_rtmp_send_status(s, "NetStream.Record.Stop", "status", rracf->id.data ? (char *) rracf->id.data : ""); } app_conf = s->app_conf; if (rracf->rec_conf) { s->app_conf = rracf->rec_conf; } v.recorder = rracf->id; ngx_rtmp_record_make_path(s, rctx, &v.path); rc = ngx_rtmp_record_done(s, &v); s->app_conf = app_conf; return rc; }
static void ngx_rtmp_record_notify_error(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx) { ngx_rtmp_record_app_conf_t *rracf = rctx->conf; rctx->failed = 1; if (!rracf->notify) { return; } ngx_rtmp_send_status(s, "NetStream.Record.Failed", "error", rracf->id.data ? (char *) rracf->id.data : ""); }
static ngx_int_t ngx_rtmp_live_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_live_app_conf_t *lacf; ngx_rtmp_live_ctx_t *ctx; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL || !lacf->live) { goto next; } /* join stream as subscriber */ ngx_rtmp_live_join(s, v->name, 0); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL) { goto next; } ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "live_play: name='%s' start=%uD duration=%uD reset=%d page_url='%V' addr_text='%V' tc_url='%V' flashver='%V'", v->name, (uint32_t) v->start, (uint32_t) v->duration, (uint32_t) v->reset, &s->page_url, s->addr_text, &s->tc_url, &s->flashver); ctx->silent = v->silent; if (!ctx->silent && !lacf->play_restart && !ctx->hls) { ngx_rtmp_send_status(s, "NetStream.Play.Start", "status", "Start live"); ngx_rtmp_send_sample_access(s); s->start = v->start; s->duration = v->duration; s->reset = v->reset; s->silent = v->silent; } ngx_rtmp_playing++; next: return next_play(s, v); }
static ngx_int_t ngx_rtmp_play_open(ngx_rtmp_session_t *s, double start) { ngx_rtmp_play_ctx_t *ctx; ngx_event_t *e; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx->file.fd == NGX_INVALID_FILE) { return NGX_ERROR; } if (ngx_rtmp_send_stream_begin(s, NGX_RTMP_MSID) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_send_status(s, "NetStream.Play.Start", "status", "Start video on demand") != NGX_OK) { return NGX_ERROR; } e = &ctx->send_evt; e->data = s; e->handler = ngx_rtmp_play_send; e->log = s->connection->log; ngx_rtmp_send_recorded(s, 1); if (ngx_rtmp_play_do_init(s) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_play_do_seek(s, start < 0 ? 0 : start) != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_play_do_start(s) != NGX_OK) { return NGX_ERROR; } return NGX_OK; }
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_live_play(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_live_app_conf_t *lacf; ngx_rtmp_live_ctx_t *ctx; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL || !lacf->live) { goto next; } ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: play: name='%s' start=%uD duration=%uD reset=%d", v->name, (uint32_t) v->start, (uint32_t) v->duration, (uint32_t) v->reset); /* join stream as subscriber */ ngx_rtmp_live_join(s, v->name, 0); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL) { goto next; } ctx->silent = v->silent; if (!ctx->silent && !lacf->play_restart) { ngx_rtmp_send_status(s, "NetStream.Play.Start", "status", "Start live"); ngx_rtmp_send_sample_access(s); } next: return next_play(s, v); }
static ngx_int_t ngx_rtmp_play_seek(ngx_rtmp_session_t *s, ngx_rtmp_seek_t *v) { ngx_rtmp_play_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); if (ctx == NULL || ctx->file.fd == NGX_INVALID_FILE) { goto next; } if (!ctx->opened) { ctx->post_seek = (ngx_uint_t) v->offset; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: post seek=%ui", ctx->post_seek); goto next; } if (ngx_rtmp_send_stream_eof(s, NGX_RTMP_MSID) != NGX_OK) { return NGX_ERROR; } ngx_rtmp_play_do_seek(s, (ngx_uint_t) v->offset); if (ngx_rtmp_send_status(s, "NetStream.Seek.Notify", "status", "Seeking") != NGX_OK) { return NGX_ERROR; } if (ngx_rtmp_send_stream_begin(s, NGX_RTMP_MSID) != NGX_OK) { return NGX_ERROR; } next: return next_seek(s, v); }
static ngx_int_t ngx_rtmp_live_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_live_app_conf_t *lacf; ngx_rtmp_live_ctx_t *ctx; lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); if (lacf == NULL || !lacf->live) { goto next; } ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "live_publish: name='%s' type='%s', page_url[len=%i]='%V', addr_text='%V', tc_url='%V'", v->name, v->type, s->page_url.len, &s->page_url, s->addr_text, &s->tc_url); /* join stream as publisher */ ngx_rtmp_live_join(s, v->name, 1); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL || !ctx->publishing) { goto next; } ctx->silent = v->silent; if (!ctx->silent) { ngx_rtmp_send_status(s, "NetStream.Publish.Start", "status", "Start publishing"); } next: return next_publish(s, v); }
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 ngx_int_t ngx_rtmp_play_next_entry(ngx_rtmp_session_t *s, ngx_rtmp_play_t *v) { ngx_rtmp_play_app_conf_t *pacf; ngx_rtmp_core_srv_conf_t *cscf; ngx_rtmp_play_ctx_t *ctx; ngx_rtmp_play_entry_t *pe; u_char *p; static u_char path[NGX_MAX_PATH + 1]; pacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_play_module); cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_play_module); for ( ;; ) { if (ctx->file.fd != NGX_INVALID_FILE) { ngx_close_file(ctx->file.fd); ctx->file.fd = NGX_INVALID_FILE; } if (ctx->file_id) { ngx_rtmp_play_cleanup_local_file(s); } ctx->nentry = (ctx->nentry == NGX_CONF_UNSET_UINT ? 0 : ctx->nentry + 1); if (ctx->nentry >= pacf->entries.nelts) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: all entries failed"); ngx_rtmp_send_status(s, "NetStream.Play.StreamNotFound", "error", "Video on demand stream not found"); break; } pe = ngx_rtmp_play_get_current_entry(s); ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: trying %s entry %ui/%uz '%V'", pe->url ? "remote" : "local", ctx->nentry + 1, pacf->entries.nelts, pe->url ? &pe->url->url : pe->root); /* open remote */ if (pe->url) { return ngx_rtmp_play_open_remote(s, v); } /* open local */ p = ngx_snprintf(path, NGX_MAX_PATH, "%V/%s%V", pe->root, v->name + ctx->pfx_size, &ctx->sfx); *p = 0; ctx->file.fd = ngx_open_file(path, NGX_FILE_RDONLY, NGX_FILE_OPEN, cscf->file_access); if (ctx->file.fd == NGX_INVALID_FILE) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, ngx_errno, "play: error opening file '%s'", path); continue; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: open local file '%s'", path); if (ngx_rtmp_play_open(s, v->start) != NGX_OK) { return NGX_ERROR; } break; } return next_play(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; 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 && pacf->url == NULL)) { 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; } ctx->file.fd = NGX_INVALID_FILE; ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "play %s: %V", pacf->url ? "remote" : "local", &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; } /* remote? */ if (pacf->url) { ctx->name.data = ngx_palloc(s->connection->pool, name.len + sfx->len); if (ctx->name.data == NULL) { return NGX_ERROR; } p = ngx_sprintf(ctx->name.data, "%V%V", &name, sfx); *p = 0; ctx->name.len = p - ctx->name.data; return ngx_rtmp_play_open_remote(s, v); } /* open local */ 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, ngx_errno, "play: error opening file '%s'", path); ngx_rtmp_send_status(s, "NetStream.Play.StreamNotFound", "error", "Video on demand stream not found"); return NGX_OK; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "play: opened local file '%s'", path); if (ngx_rtmp_play_open(s, v->start) != NGX_OK) { return NGX_ERROR; } next: return next_play(s, v); }
static ngx_int_t ngx_rtmp_record_node_open(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx) { ngx_rtmp_record_app_conf_t *rracf; ngx_err_t err; ngx_str_t path; rracf = rctx->conf; if (rctx->file.fd != NGX_INVALID_FILE) { return NGX_AGAIN; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V opening", &rracf->id); ngx_memzero(rctx, sizeof(*rctx)); rctx->conf = rracf; rctx->last = *ngx_cached_time; rctx->timestamp = ngx_cached_time->sec; ngx_rtmp_record_make_path(s, rctx, &path); ngx_memzero(&rctx->file, sizeof(rctx->file)); rctx->file.offset = 0; rctx->file.log = s->connection->log; rctx->file.fd = ngx_open_file(path.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (rctx->file.fd == NGX_INVALID_FILE) { err = ngx_errno; if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, err, "record: %V failed to open file '%V'", &rracf->id, &path); } ngx_rtmp_record_notify_error(s, rctx); return NGX_OK; } if (rracf->lock_file) { err = ngx_lock_fd(rctx->file.fd); if (err) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, err, "record: %V lock failed", &rracf->id); } } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V opened '%V'", &rracf->id, &path); if (rracf->notify) { ngx_rtmp_send_status(s, "NetStream.Record.Start", "status", rracf->id.data ? (char *) rracf->id.data : ""); } return NGX_OK; }
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_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); }
/* 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 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_record_node_open(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx) { ngx_rtmp_record_app_conf_t *rracf; ngx_rtmp_core_srv_conf_t *cscf; ngx_err_t err; ngx_str_t path; ngx_int_t mode, create_mode; u_char buf[8], *p; off_t file_size; uint32_t tag_size, mlen, timestamp; rracf = rctx->conf; tag_size = 0; if (rctx->file.fd != NGX_INVALID_FILE) { return NGX_AGAIN; } cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module); ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V opening", &rracf->id); ngx_memzero(rctx, sizeof(*rctx)); rctx->conf = rracf; rctx->last = *ngx_cached_time; rctx->timestamp = ngx_cached_time->sec; ngx_rtmp_record_make_path(s, rctx, &path); mode = rracf->append ? NGX_FILE_RDWR : NGX_FILE_WRONLY; create_mode = rracf->append ? NGX_FILE_CREATE_OR_OPEN : NGX_FILE_TRUNCATE; ngx_memzero(&rctx->file, sizeof(rctx->file)); rctx->file.offset = 0; rctx->file.log = s->connection->log; rctx->file.fd = ngx_open_file(path.data, mode, create_mode, cscf->file_access); ngx_str_set(&rctx->file.name, "recorded"); if (rctx->file.fd == NGX_INVALID_FILE) { err = ngx_errno; if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, err, "record: %V failed to open file '%V'", &rracf->id, &path); } ngx_rtmp_record_notify_error(s, rctx); return NGX_OK; } #if !(NGX_WIN32) if (rracf->lock_file) { err = ngx_lock_fd(rctx->file.fd); if (err) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, err, "record: %V lock failed", &rracf->id); } } #endif ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V opened '%V'", &rracf->id, &path); if (rracf->notify) { ngx_rtmp_send_status(s, "NetStream.Record.Start", "status", rracf->id.data ? (char *) rracf->id.data : ""); } if (rracf->append) { file_size = 0; timestamp = 0; #if (NGX_WIN32) { LONG lo, hi; lo = 0; hi = 0; lo = SetFilePointer(rctx->file.fd, lo, &hi, FILE_END); file_size = (lo == INVALID_SET_FILE_POINTER ? (off_t) -1 : (off_t) hi << 32 | (off_t) lo); } #else file_size = lseek(rctx->file.fd, 0, SEEK_END); #endif if (file_size == (off_t) -1) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, "record: %V seek failed", &rracf->id); goto done; } if (file_size < 4) { goto done; } if (ngx_read_file(&rctx->file, buf, 4, file_size - 4) != 4) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, "record: %V tag size read failed", &rracf->id); goto done; } p = (u_char *) &tag_size; p[0] = buf[3]; p[1] = buf[2]; p[2] = buf[1]; p[3] = buf[0]; if (tag_size == 0 || tag_size + 4 > file_size) { file_size = 0; goto done; } if (ngx_read_file(&rctx->file, buf, 8, file_size - tag_size - 4) != 8) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, "record: %V tag read failed", &rracf->id); goto done; } p = (u_char *) &mlen; p[0] = buf[3]; p[1] = buf[2]; p[2] = buf[1]; p[3] = 0; if (tag_size != mlen + 11) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, "record: %V tag size mismatch: " "tag_size=%uD, mlen=%uD", &rracf->id, tag_size, mlen); goto done; } p = (u_char *) ×tamp; p[3] = buf[7]; p[0] = buf[6]; p[1] = buf[5]; p[2] = buf[4]; done: rctx->file.offset = file_size; rctx->time_shift = timestamp; ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: append offset=%O, time=%uD, tag_size=%uD", file_size, timestamp, tag_size); } 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); } }