static ngx_int_t ngx_rtmp_live_delete_stream(ngx_rtmp_session_t *s, ngx_rtmp_delete_stream_t *v) { ngx_rtmp_live_ctx_t *ctx, **cctx; 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->flags & NGX_RTMP_LIVE_PUBLISHING && ctx->flags & NGX_RTMP_LIVE_PUBLISHING) { ctx->stream->flags &= ~NGX_RTMP_LIVE_PUBLISHING; } for (cctx = &ctx->stream->ctx; *cctx; cctx = &(*cctx)->next) { if (*cctx == ctx) { *cctx = ctx->next; break; } } 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) { return NGX_ERROR; } *stream = (*stream)->next; ctx->stream->next = lacf->free_streams; lacf->free_streams = ctx->stream; ctx->stream = NULL; next: return next_delete_stream(s, v); }
ngx_int_t ngx_rtmp_live_check_stream_source(ngx_rtmp_session_t *s, u_char *name) { ngx_rtmp_live_stream_t ** stream = ngx_rtmp_live_get_stream(s, name, 0); if (stream != NULL && (*stream)->publishing){ return NGX_OK; } return NGX_ERROR; }
static void ngx_rtmp_live_join(ngx_rtmp_session_t *s, u_char *name, ngx_uint_t flags) { 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 == NULL) { ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_live_ctx_t)); ctx->session = s; ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_live_module); } if (ctx->stream) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: already joined"); return; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "live: join '%s'", name); stream = ngx_rtmp_live_get_stream(s, name, 1); if (stream == NULL) { return; } if (flags & NGX_RTMP_LIVE_PUBLISHING) { if ((*stream)->flags & NGX_RTMP_LIVE_PUBLISHING) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "live: already publishing"); return; } (*stream)->flags |= NGX_RTMP_LIVE_PUBLISHING; } ctx->stream = *stream; ctx->flags = flags; ctx->next = (*stream)->ctx; (*stream)->ctx = ctx; if (lacf->buflen) { s->out_buffer = 1; } }
ngx_rtmp_live_stream_t * ngx_rtmp_live_get_stream_by_session(ngx_rtmp_session_t *s) { 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 NULL; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); if (ctx == NULL) { return NULL; } stream = ngx_rtmp_live_get_stream(s, s->name, 0); return *stream ; }
/* 关闭流 */ 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 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, **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_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 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); } }