moved metadata handlers from live to codec module & fixed setDataFrame handler to handle ffmpeg packets

This commit is contained in:
Roman Arutyunyan 2012-06-14 17:07:31 +04:00
parent fca76ef349
commit 95e2c4172a
5 changed files with 215 additions and 241 deletions

View file

@ -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;
}

View file

@ -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;

View file

@ -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;

View file

@ -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;
};

View file

@ -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("</time>");
meta = &stream->meta;
NGX_RTMP_STAT_L("<meta><width>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", meta->width) - buf);
NGX_RTMP_STAT_L("</width><height>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", meta->height) - buf);
NGX_RTMP_STAT_L("</height><framerate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", meta->frame_rate) - buf);
NGX_RTMP_STAT_L("</framerate><video>");
codec = ngx_rtmp_get_video_codec_name(meta->video_codec_id);
if (*codec) {
NGX_RTMP_STAT_ECS(codec);
}
NGX_RTMP_STAT_L("</video><audio>");
codec = ngx_rtmp_get_audio_codec_name(meta->audio_codec_id);
if (*codec) {
NGX_RTMP_STAT_ECS(codec);
}
NGX_RTMP_STAT_L("</audio></meta>\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("<client>");
@ -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("<meta><width>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->width) - buf);
NGX_RTMP_STAT_L("</width><height>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->height) - buf);
NGX_RTMP_STAT_L("</height><framerate>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%ui", codec->frame_rate) - buf);
NGX_RTMP_STAT_L("</framerate><video>");
cname = ngx_rtmp_get_video_codec_name(codec->video_codec_id);
if (*cname) {
NGX_RTMP_STAT_ECS(cname);
}
NGX_RTMP_STAT_L("</video><audio>");
cname = ngx_rtmp_get_audio_codec_name(codec->audio_codec_id);
if (*cname) {
NGX_RTMP_STAT_ECS(cname);
}
NGX_RTMP_STAT_L("</audio></meta>\r\n");
}
NGX_RTMP_STAT_L("<nclients>");
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
"%uz", nclients) - buf);