static void ngx_rtmp_record_start(ngx_rtmp_session_t *s) { ngx_rtmp_record_app_conf_t *racf; ngx_rtmp_record_rec_ctx_t *rctx; ngx_rtmp_record_ctx_t *ctx; ngx_uint_t n; racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module); if (racf == NULL || racf->rec.nelts == 0) { return; } ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module); if (ctx == NULL) { return; } ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: start"); rctx = ctx->rec.elts; for (n = 0; n < ctx->rec.nelts; ++n, ++rctx) { if (rctx->conf->flags & (NGX_RTMP_RECORD_OFF|NGX_RTMP_RECORD_MANUAL)) { continue; } ngx_rtmp_record_node_open(s, rctx); } }
ngx_int_t ngx_rtmp_record_open(ngx_rtmp_session_t *s, ngx_uint_t n, ngx_str_t *path) { ngx_rtmp_record_rec_ctx_t *rctx; ngx_int_t rc; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: #%ui manual open", n); rctx = ngx_rtmp_record_get_node_ctx(s, n); if (rctx == NULL) { return NGX_ERROR; } rc = ngx_rtmp_record_node_open(s, rctx); if (rc != NGX_OK) { return rc; } if (path) { ngx_rtmp_record_make_path(s, rctx, path); } return NGX_OK; }
static ngx_int_t ngx_rtmp_record_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { ngx_rtmp_record_app_conf_t *racf; ngx_rtmp_record_ctx_t *ctx; u_char *p; ngx_uint_t n; ngx_rtmp_record_node_ctx_t *rctx; if (s->auto_pushed) { goto next; } racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module); if (racf == NULL || racf->nodes.nelts == 0) { goto next; } if (ngx_rtmp_record_init(s) != NGX_OK) { return NGX_ERROR; } ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: publish %ui nodes", racf->nodes.nelts); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module); ngx_memcpy(ctx->name, v->name, sizeof(ctx->name)); ngx_memcpy(ctx->args, v->args, sizeof(ctx->args)); /* terminate name on /../ */ for (p = ctx->name; *p; ++p) { if (ngx_path_separator(p[0]) && p[1] == '.' && p[2] == '.' && ngx_path_separator(p[3])) { *p = 0; break; } } rctx = ctx->nodes.elts; for (n = 0; n < ctx->nodes.nelts; ++n, ++rctx) { if (rctx->conf->flags & (NGX_RTMP_RECORD_OFF|NGX_RTMP_RECORD_MANUAL)) { continue; } ngx_rtmp_record_node_open(s, rctx); } next: return next_publish(s, v); }
static ngx_int_t ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_time_t next; ngx_rtmp_header_t ch; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_int_t keyframe, brkframe; ngx_rtmp_record_app_conf_t *rracf; rracf = rctx->conf; if (rracf->flags & NGX_RTMP_RECORD_OFF) { ngx_rtmp_record_node_close(s, rctx); return NGX_OK; } keyframe = (h->type == NGX_RTMP_MSG_VIDEO) ? (ngx_rtmp_get_video_frame_type(in) == NGX_RTMP_VIDEO_KEY_FRAME) : 0; brkframe = (h->type == NGX_RTMP_MSG_VIDEO) ? keyframe : (rracf->flags & NGX_RTMP_RECORD_VIDEO) == 0; if (brkframe && ((rracf->flags & NGX_RTMP_RECORD_MANUAL) == 0 || ((rracf->flags & NGX_RTMP_RECORD_AUTO_RESTART) && rctx->file.fd != NGX_INVALID_FILE))) { if (rracf->interval != (ngx_msec_t) NGX_CONF_UNSET) { next = rctx->last; next.msec += rracf->interval; next.sec += (next.msec / 1000); next.msec %= 1000; if (ngx_cached_time->sec > next.sec || (ngx_cached_time->sec == next.sec && ngx_cached_time->msec > next.msec)) { ngx_rtmp_record_node_close(s, rctx); ngx_rtmp_record_node_open(s, rctx); } } else if (!rctx->failed) { ngx_rtmp_record_node_open(s, rctx); } } if (((rracf->flags & NGX_RTMP_RECORD_MANUAL) || !(rracf->flags & NGX_RTMP_RECORD_AUTO_RESTART)) && !brkframe && rctx->nframes == 0) { return NGX_OK; } if (rctx->file.fd == NGX_INVALID_FILE) { return NGX_OK; } if (h->type == NGX_RTMP_MSG_AUDIO && (rracf->flags & NGX_RTMP_RECORD_AUDIO) == 0) { return NGX_OK; } if (h->type == NGX_RTMP_MSG_VIDEO && (rracf->flags & NGX_RTMP_RECORD_VIDEO) == 0 && ((rracf->flags & NGX_RTMP_RECORD_KEYFRAMES) == 0 || !keyframe)) { return NGX_OK; } if (!rctx->initialized) { rctx->initialized = 1; rctx->epoch = h->timestamp - rctx->time_shift; if (rctx->file.offset == 0 && ngx_rtmp_record_write_header(&rctx->file) != NGX_OK) { ngx_rtmp_record_node_close(s, rctx); return NGX_OK; } } codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); if (codec_ctx) { ch = *h; /* AAC header */ if (!rctx->aac_header_sent && codec_ctx->aac_header && (rracf->flags & NGX_RTMP_RECORD_AUDIO)) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V writing AAC header", &rracf->id); ch.type = NGX_RTMP_MSG_AUDIO; ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->aac_header); if (ngx_rtmp_record_write_frame(s, rctx, &ch, codec_ctx->aac_header, 0) != NGX_OK) { return NGX_OK; } rctx->aac_header_sent = 1; } /* AVC header */ if (!rctx->avc_header_sent && codec_ctx->avc_header && (rracf->flags & (NGX_RTMP_RECORD_VIDEO| NGX_RTMP_RECORD_KEYFRAMES))) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V writing AVC header", &rracf->id); ch.type = NGX_RTMP_MSG_VIDEO; ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->avc_header); if (ngx_rtmp_record_write_frame(s, rctx, &ch, codec_ctx->avc_header, 0) != NGX_OK) { return NGX_OK; } rctx->avc_header_sent = 1; } } if (h->type == NGX_RTMP_MSG_VIDEO) { if (codec_ctx && codec_ctx->video_codec_id == NGX_RTMP_VIDEO_H264 && !rctx->avc_header_sent) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V skipping until H264 header", &rracf->id); return NGX_OK; } if (ngx_rtmp_get_video_frame_type(in) == NGX_RTMP_VIDEO_KEY_FRAME && ((codec_ctx && codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) || !ngx_rtmp_is_codec_header(in))) { rctx->video_key_sent = 1; } if (!rctx->video_key_sent) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V skipping until keyframe", &rracf->id); return NGX_OK; } } else { if (codec_ctx && codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC && !rctx->aac_header_sent) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V skipping until AAC header", &rracf->id); return NGX_OK; } } return ngx_rtmp_record_write_frame(s, rctx, h, in, 1); }
static ngx_int_t ngx_rtmp_record_node_av(ngx_rtmp_session_t *s, ngx_rtmp_record_node_ctx_t *rctx, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_time_t next; ngx_rtmp_header_t ch; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_int_t keyframe; ngx_rtmp_record_node_t *rc; rc = rctx->conf; if (rc->flags & NGX_RTMP_RECORD_OFF) { ngx_rtmp_record_node_close(s, rctx); return NGX_OK; } keyframe = (ngx_rtmp_get_video_frame_type(in) == NGX_RTMP_VIDEO_KEY_FRAME); if (keyframe && (rc->flags & NGX_RTMP_RECORD_MANUAL) == 0) { if (rc->interval != (ngx_msec_t) NGX_CONF_UNSET) { next = rctx->last; next.msec += rc->interval; next.sec += (next.msec / 1000); next.msec %= 1000; if (ngx_cached_time->sec > next.sec || (ngx_cached_time->sec == next.sec && ngx_cached_time->msec > next.msec)) { ngx_rtmp_record_node_close(s, rctx); ngx_rtmp_record_node_open(s, rctx); } } else { ngx_rtmp_record_node_open(s, rctx); } } if (rctx->file.fd == NGX_INVALID_FILE) { return NGX_OK; } if (h->type == NGX_RTMP_MSG_AUDIO && (rc->flags & NGX_RTMP_RECORD_AUDIO) == 0) { return NGX_OK; } if (h->type == NGX_RTMP_MSG_VIDEO && (rc->flags & NGX_RTMP_RECORD_VIDEO) == 0 && ((rc->flags & NGX_RTMP_RECORD_KEYFRAMES) == 0 || !keyframe)) { return NGX_OK; } if (rctx->file.offset == 0) { rctx->epoch = h->timestamp; if (ngx_rtmp_record_write_header(&rctx->file) != NGX_OK) { ngx_rtmp_record_node_close(s, rctx); return NGX_OK; } codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); if (codec_ctx) { ch = *h; #if 0 /* metadata */ if (codec_ctx->meta) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: writing metadata"); ch.type = NGX_RTMP_MSG_AMF_META; ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->meta); if (ngx_rtmp_record_write_frame(s, &ch, codec_ctx->meta) != NGX_OK) { return NGX_OK; } } #endif /* AAC header */ if (codec_ctx->aac_header && (rc->flags & NGX_RTMP_RECORD_AUDIO)) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V writing AAC header", &rc->id); ch.type = NGX_RTMP_MSG_AUDIO; ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->aac_header); if (ngx_rtmp_record_write_frame(s, rctx, &ch, codec_ctx->aac_header) != NGX_OK) { return NGX_OK; } } /* AVC header */ if (codec_ctx->avc_header && (rc->flags & (NGX_RTMP_RECORD_VIDEO|NGX_RTMP_RECORD_KEYFRAMES))) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V writing AVC header", &rc->id); ch.type = NGX_RTMP_MSG_VIDEO; ch.mlen = ngx_rtmp_record_get_chain_mlen(codec_ctx->avc_header); if (ngx_rtmp_record_write_frame(s, rctx, &ch, codec_ctx->avc_header) != NGX_OK) { return NGX_OK; } } } } return ngx_rtmp_record_write_frame(s, rctx, h, in); }