58 Star 431 Fork 129

GVPwinshining / nginx-http-flv-module

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
ngx_rtmp_codec_module.c 27.43 KB
一键复制 编辑 原始数据 按行查看 历史
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019
/*
* Copyright (C) Roman Arutyunyan
* Copyright (C) Winshining
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include "ngx_rtmp_codec_module.h"
#include "ngx_rtmp_live_module.h"
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rtmp_bitop.h"
#define NGX_RTMP_CODEC_META_OFF 0
#define NGX_RTMP_CODEC_META_ON 1
#define NGX_RTMP_CODEC_META_COPY 2
static void * ngx_rtmp_codec_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_codec_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child);
static ngx_int_t ngx_rtmp_codec_postconfiguration(ngx_conf_t *cf);
static ngx_int_t ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s);
static ngx_int_t ngx_rtmp_codec_copy_meta(ngx_rtmp_session_t *s,
ngx_rtmp_header_t *h, ngx_chain_t *in);
static ngx_int_t ngx_rtmp_codec_prepare_meta(ngx_rtmp_session_t *s,
uint32_t timestamp);
static ngx_int_t ngx_rtmp_codec_parse_aac_header(ngx_rtmp_session_t *s,
ngx_chain_t *in);
static ngx_int_t ngx_rtmp_codec_parse_avc_header(ngx_rtmp_session_t *s,
ngx_chain_t *in);
#if (NGX_DEBUG)
static void ngx_rtmp_codec_dump_header(ngx_rtmp_session_t *s, const char *type,
ngx_chain_t *in);
#endif
typedef struct {
ngx_uint_t meta;
} ngx_rtmp_codec_app_conf_t;
static ngx_conf_enum_t ngx_rtmp_codec_meta_slots[] = {
{ ngx_string("off"), NGX_RTMP_CODEC_META_OFF },
{ ngx_string("on"), NGX_RTMP_CODEC_META_ON },
{ ngx_string("copy"), NGX_RTMP_CODEC_META_COPY },
{ ngx_null_string, 0 }
};
static ngx_command_t ngx_rtmp_codec_commands[] = {
{ ngx_string("meta"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_enum_slot,
NGX_RTMP_APP_CONF_OFFSET,
offsetof(ngx_rtmp_codec_app_conf_t, meta),
&ngx_rtmp_codec_meta_slots },
ngx_null_command
};
static ngx_rtmp_module_t ngx_rtmp_codec_module_ctx = {
NULL, /* preconfiguration */
ngx_rtmp_codec_postconfiguration, /* postconfiguration */
NULL, /* create main configuration */
NULL, /* init main configuration */
NULL, /* create server configuration */
NULL, /* merge server configuration */
ngx_rtmp_codec_create_app_conf, /* create app configuration */
ngx_rtmp_codec_merge_app_conf /* merge app configuration */
};
ngx_module_t ngx_rtmp_codec_module = {
NGX_MODULE_V1,
&ngx_rtmp_codec_module_ctx, /* module context */
ngx_rtmp_codec_commands, /* module directives */
NGX_RTMP_MODULE, /* module type */
NULL, /* init master */
NULL, /* init module */
NULL, /* init process */
NULL, /* init thread */
NULL, /* exit thread */
NULL, /* exit process */
NULL, /* exit master */
NGX_MODULE_V1_PADDING
};
static const char *
audio_codecs[] = {
"",
"ADPCM",
"MP3",
"LinearLE",
"Nellymoser16",
"Nellymoser8",
"Nellymoser",
"G711A",
"G711U",
"",
"AAC",
"Speex",
"",
"",
"MP3-8K",
"DeviceSpecific",
"Uncompressed"
};
static const char *
video_codecs[] = {
"",
"Jpeg",
"Sorenson-H263",
"ScreenVideo",
"On2-VP6",
"On2-VP6-Alpha",
"ScreenVideo2",
"H264",
};
u_char *
ngx_rtmp_get_audio_codec_name(ngx_uint_t id)
{
return (u_char *)(id < sizeof(audio_codecs) / sizeof(audio_codecs[0])
? audio_codecs[id]
: "");
}
u_char *
ngx_rtmp_get_video_codec_name(ngx_uint_t id)
{
return (u_char *)(id < sizeof(video_codecs) / sizeof(video_codecs[0])
? video_codecs[id]
: "");
}
static ngx_uint_t
ngx_rtmp_codec_get_next_version()
{
ngx_uint_t v;
static ngx_uint_t version;
do {
v = ++version;
} while (v == 0);
return v;
}
static ngx_int_t
ngx_rtmp_codec_disconnect(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_core_srv_conf_t *cscf;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (ctx == NULL) {
return NGX_OK;
}
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
if (ctx->avc_header) {
ngx_rtmp_free_shared_chain(cscf, ctx->avc_header);
ctx->avc_header = NULL;
}
if (ctx->aac_header) {
ngx_rtmp_free_shared_chain(cscf, ctx->aac_header);
ctx->aac_header = NULL;
}
if (ctx->meta) {
ngx_rtmp_free_shared_chain(cscf, ctx->meta);
ctx->meta = NULL;
}
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_core_srv_conf_t *cscf;
ngx_rtmp_codec_ctx_t *ctx;
ngx_chain_t **header;
uint8_t fmt;
static ngx_uint_t sample_rates[] =
{ 5512, 11025, 22050, 44100 };
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;
}
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
fmt = in->buf->pos[0];
if (h->type == NGX_RTMP_MSG_AUDIO) {
ctx->audio_codec_id = (fmt & 0xf0) >> 4;
ctx->audio_channels = (fmt & 0x01) + 1;
ctx->sample_size = (fmt & 0x02) ? 2 : 1;
if (ctx->sample_rate == 0) {
ctx->sample_rate = sample_rates[(fmt & 0x0c) >> 2];
}
} else {
ctx->video_codec_id = (fmt & 0x0f);
}
/* save AVC/AAC header */
if (in->buf->last - in->buf->pos < 3) {
return NGX_OK;
}
/* PacketType = 0, FLV TAG MUST be sequence header */
/* PacketType = 1, FLV TAG MAY be AVC NALU or AAC Raw */
if (!ngx_rtmp_is_codec_header(in)) {
return NGX_OK;
}
header = NULL;
/* MUST be audio / video sequence header */
if (h->type == NGX_RTMP_MSG_AUDIO) {
if (ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) {
header = &ctx->aac_header;
if (ngx_rtmp_codec_parse_aac_header(s, in) == NGX_ERROR) {
return NGX_ERROR;
}
}
} else {
if (ctx->video_codec_id == NGX_RTMP_VIDEO_H264) {
header = &ctx->avc_header;
if (ngx_rtmp_codec_parse_avc_header(s, in) == NGX_ERROR) {
return NGX_ERROR;
}
}
}
if (header == NULL) {
return NGX_OK;
}
if (*header) {
ngx_rtmp_free_shared_chain(cscf, *header);
}
*header = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_codec_parse_aac_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
{
ngx_uint_t idx;
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_bit_reader_t br;
static ngx_uint_t aac_sample_rates[] =
{ 96000, 88200, 64000, 48000,
44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000,
7350, 0, 0, 0 };
#if (NGX_DEBUG)
ngx_rtmp_codec_dump_header(s, "aac", in);
#endif
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (in->buf->last - in->buf->pos < 4) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"codec: invalid audio codec header size=%ui",
in->buf->last - in->buf->pos);
return NGX_ERROR;
}
ngx_rtmp_bit_init_reader(&br, in->buf->pos, in->buf->last);
ngx_rtmp_bit_read(&br, 16);
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 5);
if (ctx->aac_profile == 31) {
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 6) + 32;
}
idx = (ngx_uint_t) ngx_rtmp_bit_read(&br, 4);
if (idx == 15) {
ctx->sample_rate = (ngx_uint_t) ngx_rtmp_bit_read(&br, 24);
} else {
ctx->sample_rate = aac_sample_rates[idx];
}
ctx->aac_chan_conf = (ngx_uint_t) ngx_rtmp_bit_read(&br, 4);
if (ctx->aac_profile == 5 || ctx->aac_profile == 29) {
if (ctx->aac_profile == 29) {
ctx->aac_ps = 1;
}
ctx->aac_sbr = 1;
idx = (ngx_uint_t) ngx_rtmp_bit_read(&br, 4);
if (idx == 15) {
ctx->sample_rate = (ngx_uint_t) ngx_rtmp_bit_read(&br, 24);
} else {
ctx->sample_rate = aac_sample_rates[idx];
}
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 5);
if (ctx->aac_profile == 31) {
ctx->aac_profile = (ngx_uint_t) ngx_rtmp_bit_read(&br, 6) + 32;
}
}
/* MPEG-4 Audio Specific Config
5 bits: object type
if (object type == 31)
6 bits + 32: object type
4 bits: frequency index
if (frequency index == 15)
24 bits: frequency
4 bits: channel configuration
if (object_type == 5)
4 bits: frequency index
if (frequency index == 15)
24 bits: frequency
5 bits: object type
if (object type == 31)
6 bits + 32: object type
var bits: AOT Specific Config
*/
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: aac header profile=%ui, "
"sample_rate=%ui, chan_conf=%ui",
ctx->aac_profile, ctx->sample_rate, ctx->aac_chan_conf);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_codec_parse_avc_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
{
ngx_uint_t avc_config_version;
ngx_uint_t profile_idc, width, height, crop_left, crop_right,
crop_top, crop_bottom, frame_mbs_only, n, cf_idc,
num_ref_frames;
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_bit_reader_t br;
#if (NGX_DEBUG)
ngx_rtmp_codec_dump_header(s, "avc", in);
#endif
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (in->buf->last - in->buf->pos < 18) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"codec: invalid video codec header size=%ui",
in->buf->last - in->buf->pos);
return NGX_ERROR;
}
ngx_rtmp_bit_init_reader(&br, in->buf->pos, in->buf->last);
ngx_rtmp_bit_read(&br, 40);
avc_config_version = (ngx_uint_t) ngx_rtmp_bit_read_8(&br);
if (avc_config_version == 0) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"codec: zero configurationVersion");
return NGX_ERROR;
}
ctx->avc_profile = (ngx_uint_t) ngx_rtmp_bit_read_8(&br);
ctx->avc_compat = (ngx_uint_t) ngx_rtmp_bit_read_8(&br);
ctx->avc_level = (ngx_uint_t) ngx_rtmp_bit_read_8(&br);
/* nal bytes */
ctx->avc_nal_bytes = (ngx_uint_t) ((ngx_rtmp_bit_read_8(&br) & 0x03) + 1);
if (ctx->avc_nal_bytes != 3 && ctx->avc_nal_bytes != 4) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"codec: invalid lengthSizeMinusOne value=%ui",
ctx->avc_nal_bytes - 1);
return NGX_ERROR;
}
/* nnals */
if ((ngx_rtmp_bit_read_8(&br) & 0x1f) == 0) {
return NGX_ERROR;
}
/* nal size */
ngx_rtmp_bit_read(&br, 16);
/* nal type */
if (ngx_rtmp_bit_read_8(&br) != 0x67) {
return NGX_OK;
}
/* SPS */
/* profile idc */
profile_idc = (ngx_uint_t) ngx_rtmp_bit_read(&br, 8);
/* flags */
ngx_rtmp_bit_read(&br, 8);
/* level idc */
ngx_rtmp_bit_read(&br, 8);
/* SPS id */
ngx_rtmp_bit_read_golomb(&br);
if (profile_idc == 100 || profile_idc == 110 ||
profile_idc == 122 || profile_idc == 244 || profile_idc == 44 ||
profile_idc == 83 || profile_idc == 86 || profile_idc == 118)
{
/* chroma format idc */
cf_idc = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
if (cf_idc == 3) {
/* separate color plane */
ngx_rtmp_bit_read(&br, 1);
}
/* bit depth luma - 8 */
ngx_rtmp_bit_read_golomb(&br);
/* bit depth chroma - 8 */
ngx_rtmp_bit_read_golomb(&br);
/* qpprime y zero transform bypass */
ngx_rtmp_bit_read(&br, 1);
/* seq scaling matrix present */
if (ngx_rtmp_bit_read(&br, 1)) {
for (n = 0; n < (cf_idc != 3 ? 8u : 12u); n++) {
/* seq scaling list present */
if (ngx_rtmp_bit_read(&br, 1)) {
/* TODO: scaling_list()
if (n < 6) {
} else {
}
*/
}
}
}
}
/* log2 max frame num */
ngx_rtmp_bit_read_golomb(&br);
/* pic order cnt type */
switch (ngx_rtmp_bit_read_golomb(&br)) {
case 0:
/* max pic order cnt */
ngx_rtmp_bit_read_golomb(&br);
break;
case 1:
/* delta pic order alwys zero */
ngx_rtmp_bit_read(&br, 1);
/* offset for non-ref pic */
ngx_rtmp_bit_read_golomb(&br);
/* offset for top to bottom field */
ngx_rtmp_bit_read_golomb(&br);
/* num ref frames in pic order */
num_ref_frames = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
for (n = 0; n < num_ref_frames; n++) {
/* offset for ref frame */
ngx_rtmp_bit_read_golomb(&br);
}
}
/* num ref frames */
ctx->avc_ref_frames = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
/* gaps in frame num allowed */
ngx_rtmp_bit_read(&br, 1);
/* pic width in mbs - 1 */
width = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
/* pic height in map units - 1 */
height = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
/* frame mbs only flag */
frame_mbs_only = (ngx_uint_t) ngx_rtmp_bit_read(&br, 1);
if (!frame_mbs_only) {
/* mbs adaprive frame field */
ngx_rtmp_bit_read(&br, 1);
}
/* direct 8x8 inference flag */
ngx_rtmp_bit_read(&br, 1);
/* frame cropping */
if (ngx_rtmp_bit_read(&br, 1)) {
crop_left = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
crop_right = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
crop_top = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
crop_bottom = (ngx_uint_t) ngx_rtmp_bit_read_golomb(&br);
} else {
crop_left = 0;
crop_right = 0;
crop_top = 0;
crop_bottom = 0;
}
ctx->width = (width + 1) * 16 - (crop_left + crop_right) * 2;
ctx->height = (2 - frame_mbs_only) * (height + 1) * 16 -
(crop_top + crop_bottom) * 2;
ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: avc header "
"profile=%ui, compat=%ui, level=%ui, "
"nal_bytes=%ui, ref_frames=%ui, width=%ui, height=%ui",
ctx->avc_profile, ctx->avc_compat, ctx->avc_level,
ctx->avc_nal_bytes, ctx->avc_ref_frames,
ctx->width, ctx->height);
return NGX_OK;
}
#if (NGX_DEBUG)
static void
ngx_rtmp_codec_dump_header(ngx_rtmp_session_t *s, const char *type,
ngx_chain_t *in)
{
u_char buf[256], *p, *pp;
u_char hex[] = "0123456789abcdef";
for (pp = buf, p = in->buf->pos;
p < in->buf->last && pp < buf + sizeof(buf) - 2;
++p)
{
*pp++ = hex[*p >> 4];
*pp++ = hex[*p & 0x0f];
}
*pp = 0;
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: %s header %s", type, buf);
}
#endif
static ngx_int_t
ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
{
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_core_srv_conf_t *cscf;
ngx_int_t rc;
static struct {
double width;
double height;
double duration;
double frame_rate;
double video_data_rate;
double video_codec_id;
double audio_data_rate;
double audio_codec_id;
u_char profile[32];
u_char level[32];
} v;
static ngx_rtmp_amf_elt_t out_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("Server"),
"NGINX HTTP-FLV (https://github.com/winshining/nginx-http-flv-module)", 0 },
{ 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("displayWidth"),
&v.width, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("displayHeight"),
&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("fps"),
&v.frame_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videodatarate"),
&v.video_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("videocodecid"),
&v.video_codec_id, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiodatarate"),
&v.audio_data_rate, 0 },
{ NGX_RTMP_AMF_NUMBER,
ngx_string("audiocodecid"),
&v.audio_codec_id, 0 },
{ NGX_RTMP_AMF_STRING,
ngx_string("profile"),
&v.profile, sizeof(v.profile) },
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
&v.level, sizeof(v.level) },
};
static ngx_rtmp_amf_elt_t out_elts[] = {
{ NGX_RTMP_AMF_STRING,
ngx_null_string,
"onMetaData", 0 },
{ NGX_RTMP_AMF_OBJECT,
ngx_null_string,
out_inf, sizeof(out_inf) },
};
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (ctx == NULL) {
return NGX_OK;
}
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
if (ctx->meta) {
ngx_rtmp_free_shared_chain(cscf, ctx->meta);
ctx->meta = NULL;
}
v.width = ctx->width;
v.height = ctx->height;
v.duration = ctx->duration;
v.frame_rate = ctx->frame_rate;
v.video_data_rate = ctx->video_data_rate;
v.video_codec_id = ctx->video_codec_id;
v.audio_data_rate = ctx->audio_data_rate;
v.audio_codec_id = ctx->audio_codec_id;
ngx_memcpy(v.profile, ctx->profile, sizeof(ctx->profile));
ngx_memcpy(v.level, ctx->level, sizeof(ctx->level));
rc = ngx_rtmp_append_amf(s, &ctx->meta, NULL, out_elts,
sizeof(out_elts) / sizeof(out_elts[0]));
if (rc != NGX_OK || ctx->meta == NULL) {
return NGX_ERROR;
}
return ngx_rtmp_codec_prepare_meta(s, 0);
}
static ngx_int_t
ngx_rtmp_codec_copy_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
ngx_rtmp_codec_ctx_t *ctx;
ngx_rtmp_core_srv_conf_t *cscf;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
if (ctx->meta) {
ngx_rtmp_free_shared_chain(cscf, ctx->meta);
}
ctx->meta = ngx_rtmp_append_shared_bufs(cscf, NULL, in);
if (ctx->meta == NULL) {
return NGX_ERROR;
}
return ngx_rtmp_codec_prepare_meta(s, h->timestamp);
}
static ngx_int_t
ngx_rtmp_codec_prepare_meta(ngx_rtmp_session_t *s, uint32_t timestamp)
{
ngx_rtmp_header_t h;
ngx_rtmp_codec_ctx_t *ctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
ngx_memzero(&h, sizeof(h));
h.csid = NGX_RTMP_CSID_AMF;
h.msid = NGX_RTMP_MSID;
h.type = NGX_RTMP_MSG_AMF_META;
h.timestamp = timestamp;
ngx_rtmp_prepare_message(s, &h, NULL, ctx->meta);
ctx->meta_version = ngx_rtmp_codec_get_next_version();
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_codec_meta_data(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in)
{
uint16_t len;
ngx_uint_t skip;
u_char *p;
ngx_rtmp_codec_app_conf_t *cacf;
ngx_rtmp_codec_ctx_t *ctx;
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];
u_char profile[32];
u_char level[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("fps"),
&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) },
{ NGX_RTMP_AMF_STRING,
ngx_string("profile"),
&v.profile, sizeof(v.profile) },
{ NGX_RTMP_AMF_STRING,
ngx_string("level"),
&v.level, sizeof(v.level) },
};
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) },
};
cacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_codec_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);
}
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 (in->buf->last > in->buf->pos
&& in->buf->pos - in->buf->start >= 13
&& in->buf->pos[-13] == NGX_RTMP_AMF_STRING)
{
p = (u_char *) &len;
*p++ = in->buf->pos[-11];
*p++ = in->buf->pos[-12];
if (ngx_strncasecmp(in->buf->pos - 10,
(u_char *) "onMetaData", len) == 0)
{
in->buf->pos -= 13;
}
}
/* FFmpeg sends a string in front of actual 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 = (ngx_uint_t) v.width;
ctx->height = (ngx_uint_t) v.height;
ctx->duration = v.duration;
ctx->frame_rate = v.frame_rate;
ctx->video_data_rate = v.video_data_rate;
ctx->video_codec_id = (ngx_uint_t) 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 : (ngx_uint_t) v.audio_codec_id_n);
ngx_memcpy(ctx->profile, v.profile, sizeof(v.profile));
ngx_memcpy(ctx->level, v.level, sizeof(v.level));
ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"codec: data frame: "
"width=%ui height=%ui duration=%.3f frame_rate=%.3f "
"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);
switch (cacf->meta) {
case NGX_RTMP_CODEC_META_ON:
return ngx_rtmp_codec_reconstruct_meta(s);
case NGX_RTMP_CODEC_META_COPY:
return ngx_rtmp_codec_copy_meta(s, h, in);
}
/* NGX_RTMP_CODEC_META_OFF */
return NGX_OK;
}
static void *
ngx_rtmp_codec_create_app_conf(ngx_conf_t *cf)
{
ngx_rtmp_codec_app_conf_t *cacf;
cacf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_codec_app_conf_t));
if (cacf == NULL) {
return NULL;
}
cacf->meta = NGX_CONF_UNSET_UINT;
return cacf;
}
static char *
ngx_rtmp_codec_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
{
ngx_rtmp_codec_app_conf_t *prev = parent;
ngx_rtmp_codec_app_conf_t *conf = child;
ngx_conf_merge_uint_value(conf->meta, prev->meta, NGX_RTMP_CODEC_META_ON);
return NGX_CONF_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);
h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]);
*h = ngx_rtmp_codec_av;
h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_VIDEO]);
*h = ngx_rtmp_codec_av;
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;
}
C
1
https://gitee.com/winshining/nginx-http-flv-module.git
git@gitee.com:winshining/nginx-http-flv-module.git
winshining
nginx-http-flv-module
nginx-http-flv-module
master

搜索帮助