diff --git a/ngx_rtmp_codec_module.c b/ngx_rtmp_codec_module.c index 3ec3950..a727f50 100644 --- a/ngx_rtmp_codec_module.c +++ b/ngx_rtmp_codec_module.c @@ -146,11 +146,31 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_header_t ch, lh; ngx_uint_t *version; + + 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; + } else { + ctx->video_codec_id = (fmt & 0x0f); + } + /* save AVC/AAC header */ - if ((h->type != NGX_RTMP_MSG_AUDIO - && h->type != NGX_RTMP_MSG_VIDEO) - || in->buf->last - in->buf->pos < 2) - { + if (in->buf->last - in->buf->pos < 2) { return NGX_OK; } @@ -160,16 +180,9 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module); - 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); - } - - fmt = in->buf->pos[0]; header = NULL; if (h->type == NGX_RTMP_MSG_AUDIO) { - if (((fmt & 0xf0) >> 4) == NGX_RTMP_AUDIO_AAC) { + if (ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) { header = &ctx->aac_header; pheader = &ctx->aac_pheader; version = &ctx->aac_version; @@ -177,7 +190,7 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, "codec: AAC header arrived"); } } else { - if ((fmt & 0x0f) == NGX_RTMP_VIDEO_H264) { + if (ctx->video_codec_id == NGX_RTMP_VIDEO_H264) { header = &ctx->avc_header; pheader = &ctx->avc_pheader; version = &ctx->avc_version; @@ -219,11 +232,148 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } +static ngx_int_t +ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_rtmp_codec_ctx_t *ctx; + ngx_uint_t skip; + + static struct { + double width; + double height; + double duration; + double frame_rate; + double video_data_rate; + double video_codec_id_n; + u_char video_codec_id_s[32]; + double audio_data_rate; + double audio_codec_id_n; + u_char audio_codec_id_s[32]; + } v; + + static ngx_rtmp_amf_elt_t in_video_codec_id[] = { + + { NGX_RTMP_AMF_NUMBER, + ngx_null_string, + &v.video_codec_id_n, 0 }, + + { NGX_RTMP_AMF_STRING, + ngx_null_string, + &v.video_codec_id_s, sizeof(v.video_codec_id_s) }, + }; + + static ngx_rtmp_amf_elt_t in_audio_codec_id[] = { + + { NGX_RTMP_AMF_NUMBER, + ngx_null_string, + &v.audio_codec_id_n, 0 }, + + { NGX_RTMP_AMF_STRING, + ngx_null_string, + &v.audio_codec_id_s, sizeof(v.audio_codec_id_s) }, + }; + + static ngx_rtmp_amf_elt_t in_inf[] = { + + { NGX_RTMP_AMF_NUMBER, + ngx_string("width"), + &v.width, 0 }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("height"), + &v.height, 0 }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("duration"), + &v.duration, 0 }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("framerate"), + &v.frame_rate, 0 }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("videodatarate"), + &v.video_data_rate, 0 }, + + { NGX_RTMP_AMF_VARIANT, + ngx_string("videocodecid"), + in_video_codec_id, sizeof(in_video_codec_id) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("audiodatarate"), + &v.audio_data_rate, 0 }, + + { NGX_RTMP_AMF_VARIANT, + ngx_string("audiocodecid"), + in_audio_codec_id, sizeof(in_audio_codec_id) }, + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_null_string, + NULL, 0 }, + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + in_inf, sizeof(in_inf) }, + }; + + 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); + } + + ngx_memzero(&v, sizeof(v)); + + /* use -1 as a sign of unchanged data; + * 0 is a valid value for uncompressed audio */ + v.audio_codec_id_n = -1; + + /* FFmpeg sends a string in front of actal metadata; ignore it */ + skip = !(in->buf->last > in->buf->pos + && *in->buf->pos == NGX_RTMP_AMF_STRING); + if (ngx_rtmp_receive_amf(s, in, in_elts + skip, + sizeof(in_elts) / sizeof(in_elts[0]) - skip)) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "codec: error parsing data frame"); + return NGX_OK; + } + + ctx->width = v.width; + ctx->height = v.height; + ctx->duration = v.duration; + ctx->frame_rate = v.frame_rate; + ctx->video_data_rate = v.video_data_rate; + ctx->video_codec_id = v.video_codec_id_n; + ctx->audio_data_rate = v.audio_data_rate; + ctx->audio_codec_id = (v.audio_codec_id_n == -1 + ? 0 : v.audio_codec_id_n == 0 + ? NGX_RTMP_AUDIO_UNCOMPRESSED : v.audio_codec_id_n); + + ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "codec: data frame: " + "width=%ui height=%ui duration=%ui frame_rate=%ui " + "video=%s (%ui) audio=%s (%ui)", + ctx->width, ctx->height, ctx->duration, ctx->frame_rate, + ngx_rtmp_get_video_codec_name(ctx->video_codec_id), + ctx->video_codec_id, + ngx_rtmp_get_audio_codec_name(ctx->audio_codec_id), + ctx->audio_codec_id); + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf) { ngx_rtmp_core_main_conf_t *cmcf; ngx_rtmp_handler_pt *h; + ngx_rtmp_amf_handler_t *ch; cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); @@ -236,5 +386,21 @@ ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf) h = ngx_array_push(&cmcf->events[NGX_RTMP_DISCONNECT]); *h = ngx_rtmp_codec_disconnect; + /* register metadata handler */ + ch = ngx_array_push(&cmcf->amf); + if (ch == NULL) { + return NGX_ERROR; + } + ngx_str_set(&ch->name, "@setDataFrame"); + ch->handler = ngx_rtmp_codec_meta_data; + + ch = ngx_array_push(&cmcf->amf); + if (ch == NULL) { + return NGX_ERROR; + } + ngx_str_set(&ch->name, "onMetaData"); + ch->handler = ngx_rtmp_codec_meta_data; + + return NGX_OK; } diff --git a/ngx_rtmp_codec_module.h b/ngx_rtmp_codec_module.h index 1d2d308..0b802e1 100644 --- a/ngx_rtmp_codec_module.h +++ b/ngx_rtmp_codec_module.h @@ -47,6 +47,15 @@ u_char * ngx_rtmp_get_video_codec_name(ngx_uint_t id); typedef struct { + ngx_uint_t width; + ngx_uint_t height; + ngx_uint_t duration; + ngx_uint_t frame_rate; + ngx_uint_t video_data_rate; + ngx_uint_t video_codec_id; + ngx_uint_t audio_data_rate; + ngx_uint_t audio_codec_id; + ngx_uint_t avc_version; ngx_uint_t aac_version; diff --git a/ngx_rtmp_live_module.c b/ngx_rtmp_live_module.c index fc3f45a..198f898 100644 --- a/ngx_rtmp_live_module.c +++ b/ngx_rtmp_live_module.c @@ -290,7 +290,6 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_header_t ch, lh; ngx_uint_t prio, peer_prio; ngx_uint_t peers, dropped_peers; - uint8_t flv_fmt; size_t header_offset; ngx_uint_t header_version; @@ -351,15 +350,6 @@ ngx_rtmp_live_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, peers = 0; dropped_peers = 0; - if (in->buf->last - in->buf->pos >= 1) { - flv_fmt = *in->buf->pos; - if (h->type == NGX_RTMP_MSG_AUDIO) { - ctx->stream->meta.audio_codec_id = (flv_fmt & 0xf0) >> 4; - } else { - ctx->stream->meta.video_codec_id = (flv_fmt & 0x0f); - } - } - codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); header_out = NULL; pheader_out = NULL; @@ -489,192 +479,14 @@ next: } -static ngx_int_t -ngx_rtmp_live_ext_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in, ngx_uint_t skip) -{ - ngx_rtmp_live_app_conf_t *lacf; - ngx_rtmp_live_ctx_t *ctx; - ngx_rtmp_live_meta_t *meta; - - static struct { - double width; - double height; - double duration; - double frame_rate; - double video_data_rate; - double video_codec_id_n; - u_char video_codec_id_s[32]; - double audio_data_rate; - double audio_codec_id_n; - u_char audio_codec_id_s[32]; - } v; - - static ngx_rtmp_amf_elt_t in_video_codec_id[] = { - - { NGX_RTMP_AMF_NUMBER, - ngx_null_string, - &v.video_codec_id_n, 0 }, - - { NGX_RTMP_AMF_STRING, - ngx_null_string, - &v.video_codec_id_s, sizeof(v.video_codec_id_s) }, - }; - - static ngx_rtmp_amf_elt_t in_audio_codec_id[] = { - - { NGX_RTMP_AMF_NUMBER, - ngx_null_string, - &v.audio_codec_id_n, 0 }, - - { NGX_RTMP_AMF_STRING, - ngx_null_string, - &v.audio_codec_id_s, sizeof(v.audio_codec_id_s) }, - }; - - static ngx_rtmp_amf_elt_t in_inf[] = { - - { NGX_RTMP_AMF_NUMBER, - ngx_string("width"), - &v.width, 0 }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("height"), - &v.height, 0 }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("duration"), - &v.duration, 0 }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("framerate"), - &v.frame_rate, 0 }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("videodatarate"), - &v.video_data_rate, 0 }, - - { NGX_RTMP_AMF_VARIANT, - ngx_string("videocodecid"), - in_video_codec_id, sizeof(in_video_codec_id) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("audiodatarate"), - &v.audio_data_rate, 0 }, - - { NGX_RTMP_AMF_VARIANT, - ngx_string("audiocodecid"), - in_audio_codec_id, sizeof(in_audio_codec_id) }, - }; - - static ngx_rtmp_amf_elt_t in_elts[] = { - - { NGX_RTMP_AMF_STRING, - ngx_null_string, - NULL, 0 }, - - { NGX_RTMP_AMF_OBJECT, - ngx_null_string, - in_inf, sizeof(in_inf) }, - }; - - - lacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_live_module); - if (lacf == NULL || !lacf->live) { - return NGX_OK; - } - - ngx_memzero(&v, sizeof(v)); - - /* use -1 as a sign of unchanged data; - * 0 is a valid value for uncompressed audio */ - v.audio_codec_id_n = -1; - - if (ngx_rtmp_receive_amf(s, in, in_elts + skip, - sizeof(in_elts) / sizeof(in_elts[0]) - skip)) - { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "live: error parsing data frame"); - return NGX_OK; - } - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); - if (ctx == NULL) { - return NGX_OK; - } - - if ((ctx->flags & NGX_RTMP_LIVE_PUBLISHING) == 0) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "live: received data stream from non-publisher"); - return NGX_OK; - } - - meta = &ctx->stream->meta; - meta->width = v.width; - meta->height = v.height; - meta->duration = v.duration; - meta->frame_rate = v.frame_rate; - meta->video_data_rate = v.video_data_rate; - meta->video_codec_id = v.video_codec_id_n; - meta->audio_data_rate = v.audio_data_rate; - meta->audio_codec_id = (v.audio_codec_id_n == -1 - ? 0 : v.audio_codec_id_n == 0 - ? NGX_RTMP_AUDIO_UNCOMPRESSED : v.audio_codec_id_n); - - ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "live: data frame: " - "width=%ui height=%ui duration=%ui frame_rate=%ui " - "video=%s (%ui) audio=%s (%ui)", - meta->width, meta->height, meta->duration, meta->frame_rate, - ngx_rtmp_get_video_codec_name(meta->video_codec_id), - meta->video_codec_id, - ngx_rtmp_get_audio_codec_name(meta->audio_codec_id), - meta->audio_codec_id); - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_live_data_frame(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) -{ - return ngx_rtmp_live_ext_meta_data(s, h, in, 0); -} - - -static ngx_int_t -ngx_rtmp_live_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) -{ - return ngx_rtmp_live_ext_meta_data(s, h, in, 1); -} - - static ngx_int_t ngx_rtmp_live_postconfiguration(ngx_conf_t *cf) { ngx_rtmp_core_main_conf_t *cmcf; ngx_rtmp_handler_pt *h; - ngx_rtmp_amf_handler_t *ch; cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); - /* register metadata handler */ - ch = ngx_array_push(&cmcf->amf); - if (ch == NULL) { - return NGX_ERROR; - } - ngx_str_set(&ch->name, "@setDataFrame"); - ch->handler = ngx_rtmp_live_data_frame; - - ch = ngx_array_push(&cmcf->amf); - if (ch == NULL) { - return NGX_ERROR; - } - ngx_str_set(&ch->name, "onMetaData"); - ch->handler = ngx_rtmp_live_meta_data; - /* register raw event handlers */ h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]); *h = ngx_rtmp_live_av; diff --git a/ngx_rtmp_live_module.h b/ngx_rtmp_live_module.h index ac5e7ac..73b64ef 100644 --- a/ngx_rtmp_live_module.h +++ b/ngx_rtmp_live_module.h @@ -22,18 +22,6 @@ #define NGX_RTMP_LIVE_MSID 1 -typedef struct { - ngx_uint_t width; - ngx_uint_t height; - ngx_uint_t duration; - ngx_uint_t frame_rate; - ngx_uint_t video_data_rate; - ngx_uint_t video_codec_id; - ngx_uint_t audio_data_rate; - ngx_uint_t audio_codec_id; -} ngx_rtmp_live_meta_t; - - typedef struct ngx_rtmp_live_ctx_s ngx_rtmp_live_ctx_t; typedef struct ngx_rtmp_live_stream_s ngx_rtmp_live_stream_t; @@ -61,7 +49,6 @@ struct ngx_rtmp_live_stream_s { ngx_uint_t flags; ngx_rtmp_bandwidth_t bw_in; ngx_rtmp_bandwidth_t bw_out; - ngx_rtmp_live_meta_t meta; ngx_msec_t epoch; }; diff --git a/ngx_rtmp_stat_module.c b/ngx_rtmp_stat_module.c index f55cc90..f3b609f 100644 --- a/ngx_rtmp_stat_module.c +++ b/ngx_rtmp_stat_module.c @@ -250,7 +250,7 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll, ngx_rtmp_live_app_conf_t *lacf) { ngx_rtmp_live_stream_t *stream; - ngx_rtmp_live_meta_t *meta; + ngx_rtmp_codec_ctx_t *codec; ngx_rtmp_live_ctx_t *ctx; ngx_rtmp_session_t *s; ngx_int_t n; @@ -258,7 +258,7 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll, ngx_int_t publishing; u_char buf[NGX_OFF_T_LEN + 1]; ngx_rtmp_stat_loc_conf_t *slcf; - u_char *codec; + u_char *cname; slcf = ngx_http_get_module_loc_conf(r, ngx_rtmp_stat_module); @@ -279,36 +279,12 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll, "%M", ngx_current_msec - stream->epoch) - buf); NGX_RTMP_STAT_L(""); - meta = &stream->meta; - NGX_RTMP_STAT_L(""); - NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), - "%ui", meta->width) - buf); - NGX_RTMP_STAT_L(""); - NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), - "%ui", meta->height) - buf); - NGX_RTMP_STAT_L(""); - NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), - "%ui", meta->frame_rate) - buf); - NGX_RTMP_STAT_L("\r\n"); - ngx_rtmp_stat_bw(r, lll, &stream->bw_in, &stream->bw_out); nclients = 0; + codec = NULL; for (ctx = stream->ctx; ctx; ctx = ctx->next, ++nclients) { s = ctx->session; - /* TODO: add - * 1) session start time - * 2) drop stats */ if (slcf->stat & NGX_RTMP_STAT_CLIENTS) { NGX_RTMP_STAT_L(""); @@ -356,10 +332,34 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll, } if (ctx->flags & NGX_RTMP_LIVE_PUBLISHING) { publishing = 1; + codec = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); } } total_nclients += nclients; + if (codec) { + NGX_RTMP_STAT_L(""); + NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), + "%ui", codec->width) - buf); + NGX_RTMP_STAT_L(""); + NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), + "%ui", codec->height) - buf); + NGX_RTMP_STAT_L(""); + NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), + "%ui", codec->frame_rate) - buf); + NGX_RTMP_STAT_L("\r\n"); + } + NGX_RTMP_STAT_L(""); NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf), "%uz", nclients) - buf);