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_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_rtmp_record_ctx_t *ctx; ngx_rtmp_record_app_conf_t *racf; ngx_time_t next; ngx_rtmp_header_t ch; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_int_t keyframe; racf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_record_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module); if (racf == NULL || ctx == NULL || racf->flags & NGX_RTMP_RECORD_OFF) { return NGX_OK; } keyframe = (ngx_rtmp_get_video_frame_type(in) == NGX_RTMP_VIDEO_KEY_FRAME); if (keyframe && racf->interval != (ngx_msec_t)NGX_CONF_UNSET) { next = ctx->last; next.msec += racf->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_close(s); if (ngx_rtmp_record_open(s) != NGX_OK) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, 0, "record: failed"); } } } if (ctx->file.fd == NGX_INVALID_FILE) { return NGX_OK; } /* filter frames */ if (h->type == NGX_RTMP_MSG_AUDIO && (racf->flags & NGX_RTMP_RECORD_AUDIO) == 0) { return NGX_OK; } if (h->type == NGX_RTMP_MSG_VIDEO && (racf->flags & NGX_RTMP_RECORD_VIDEO) == 0 && ((racf->flags & NGX_RTMP_RECORD_KEYFRAMES) == 0 || !keyframe)) { return NGX_OK; } if (ctx->file.offset == 0) { ctx->epoch = h->timestamp; if (ngx_rtmp_record_write_header(&ctx->file) != NGX_OK) { ngx_rtmp_record_close(s); 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 && (racf->flags & NGX_RTMP_RECORD_AUDIO)) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: writing AAC header"); 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, &ch, codec_ctx->aac_header) != NGX_OK) { return NGX_OK; } } /* AVC header */ if (codec_ctx->avc_header && (racf->flags & (NGX_RTMP_RECORD_VIDEO|NGX_RTMP_RECORD_KEYFRAMES))) { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: writing AVC header"); 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, &ch, codec_ctx->avc_header) != NGX_OK) { return NGX_OK; } } } } return ngx_rtmp_record_write_frame(s, h, in); }