mirror of
https://github.com/zotanmew/nginx-rtmp-module.git
synced 2024-04-28 18:53:45 +02:00
implemented avc sps parser & improved dash & stats
This commit is contained in:
parent
aa4bcf89d7
commit
18fa7a5016
2
config
2
config
|
@ -43,6 +43,7 @@ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \
|
||||||
$ngx_addon_dir/ngx_rtmp_record_module.h \
|
$ngx_addon_dir/ngx_rtmp_record_module.h \
|
||||||
$ngx_addon_dir/ngx_rtmp_relay_module.h \
|
$ngx_addon_dir/ngx_rtmp_relay_module.h \
|
||||||
$ngx_addon_dir/ngx_rtmp_streams.h \
|
$ngx_addon_dir/ngx_rtmp_streams.h \
|
||||||
|
$ngx_addon_dir/ngx_rtmp_bitop.h \
|
||||||
$ngx_addon_dir/hls/ngx_rtmp_mpegts.h \
|
$ngx_addon_dir/hls/ngx_rtmp_mpegts.h \
|
||||||
$ngx_addon_dir/dash/ngx_rtmp_mp4.h \
|
$ngx_addon_dir/dash/ngx_rtmp_mp4.h \
|
||||||
"
|
"
|
||||||
|
@ -77,6 +78,7 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \
|
||||||
$ngx_addon_dir/ngx_rtmp_notify_module.c \
|
$ngx_addon_dir/ngx_rtmp_notify_module.c \
|
||||||
$ngx_addon_dir/ngx_rtmp_log_module.c \
|
$ngx_addon_dir/ngx_rtmp_log_module.c \
|
||||||
$ngx_addon_dir/ngx_rtmp_limit_module.c \
|
$ngx_addon_dir/ngx_rtmp_limit_module.c \
|
||||||
|
$ngx_addon_dir/ngx_rtmp_bitop.c \
|
||||||
$ngx_addon_dir/hls/ngx_rtmp_hls_module.c \
|
$ngx_addon_dir/hls/ngx_rtmp_hls_module.c \
|
||||||
$ngx_addon_dir/dash/ngx_rtmp_dash_module.c \
|
$ngx_addon_dir/dash/ngx_rtmp_dash_module.c \
|
||||||
$ngx_addon_dir/hls/ngx_rtmp_mpegts.c \
|
$ngx_addon_dir/hls/ngx_rtmp_mpegts.c \
|
||||||
|
|
|
@ -511,14 +511,18 @@ ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b)
|
||||||
|
|
||||||
/* assume config fits one chunk (highly probable) */
|
/* assume config fits one chunk (highly probable) */
|
||||||
|
|
||||||
/* check for start code */
|
/*
|
||||||
for (p = in->buf->pos; p <= in->buf->last; p++) {
|
* Skip:
|
||||||
if (*p == 0x01) {
|
* - flv fmt
|
||||||
break;
|
* - H264 CONF/PICT (0x00)
|
||||||
}
|
* - 0
|
||||||
}
|
* - 0
|
||||||
|
* - 0
|
||||||
|
*/
|
||||||
|
|
||||||
if (in->buf->last - p > 0) {
|
p = in->buf->pos + 5;
|
||||||
|
|
||||||
|
if (p < in->buf->last) {
|
||||||
ngx_rtmp_mp4_data(b, p, (size_t) (in->buf->last - p));
|
ngx_rtmp_mp4_data(b, p, (size_t) (in->buf->last - p));
|
||||||
} else {
|
} else {
|
||||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||||
|
|
|
@ -657,7 +657,7 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
|
||||||
nnals &= 0x1f; /* 5lsb */
|
nnals &= 0x1f; /* 5lsb */
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
"h264: SPS number: %uz", nnals);
|
"hls: SPS number: %uz", nnals);
|
||||||
|
|
||||||
/* SPS */
|
/* SPS */
|
||||||
for (n = 0; ; ++n) {
|
for (n = 0; ; ++n) {
|
||||||
|
@ -671,12 +671,12 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
|
||||||
ngx_rtmp_rmemcpy(&len, &rlen, 2);
|
ngx_rtmp_rmemcpy(&len, &rlen, 2);
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
"h264: header NAL length: %uz", (size_t) len);
|
"hls: header NAL length: %uz", (size_t) len);
|
||||||
|
|
||||||
/* AnnexB prefix */
|
/* AnnexB prefix */
|
||||||
if (out->end - out->last < 4) {
|
if (out->end - out->last < 4) {
|
||||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||||
"h264: too small buffer for header NAL size");
|
"hls: too small buffer for header NAL size");
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -688,7 +688,7 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
|
||||||
/* NAL body */
|
/* NAL body */
|
||||||
if (out->end - out->last < len) {
|
if (out->end - out->last < len) {
|
||||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||||
"h264: too small buffer for header NAL");
|
"hls: too small buffer for header NAL");
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -709,7 +709,7 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out)
|
||||||
}
|
}
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
"h264: PPS number: %uz", nnals);
|
"hls: PPS number: %uz", nnals);
|
||||||
}
|
}
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
|
@ -1612,40 +1612,6 @@ ngx_rtmp_hls_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
|
||||||
ngx_rtmp_hls_get_nal_bytes(ngx_rtmp_session_t *s)
|
|
||||||
{
|
|
||||||
ngx_rtmp_codec_ctx_t *codec_ctx;
|
|
||||||
ngx_chain_t *cl;
|
|
||||||
u_char *p;
|
|
||||||
uint8_t nal_bytes;
|
|
||||||
|
|
||||||
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
|
|
||||||
|
|
||||||
cl = codec_ctx->avc_header;
|
|
||||||
|
|
||||||
p = cl->buf->pos;
|
|
||||||
|
|
||||||
if (ngx_rtmp_hls_copy(s, NULL, &p, 9, &cl) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* NAL size length (1,2,4) */
|
|
||||||
if (ngx_rtmp_hls_copy(s, &nal_bytes, &p, 1, &cl) != NGX_OK) {
|
|
||||||
return NGX_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
nal_bytes &= 0x03; /* 2 lsb */
|
|
||||||
|
|
||||||
++nal_bytes;
|
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
|
||||||
"hls: NAL bytes: %ui", (ngx_uint_t) nal_bytes);
|
|
||||||
|
|
||||||
return nal_bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
static ngx_int_t
|
static ngx_int_t
|
||||||
ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
ngx_chain_t *in)
|
ngx_chain_t *in)
|
||||||
|
@ -1660,7 +1626,7 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
uint32_t cts;
|
uint32_t cts;
|
||||||
ngx_rtmp_mpegts_frame_t frame;
|
ngx_rtmp_mpegts_frame_t frame;
|
||||||
ngx_uint_t nal_bytes;
|
ngx_uint_t nal_bytes;
|
||||||
ngx_int_t aud_sent, sps_pps_sent, rc, boundary;
|
ngx_int_t aud_sent, sps_pps_sent, boundary;
|
||||||
static u_char buffer[NGX_RTMP_HLS_BUFSIZE];
|
static u_char buffer[NGX_RTMP_HLS_BUFSIZE];
|
||||||
|
|
||||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||||
|
@ -1719,14 +1685,7 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
out.pos = out.start;
|
out.pos = out.start;
|
||||||
out.last = out.pos;
|
out.last = out.pos;
|
||||||
|
|
||||||
rc = ngx_rtmp_hls_get_nal_bytes(s);
|
nal_bytes = codec_ctx->avc_nal_bytes;
|
||||||
if (rc < 0) {
|
|
||||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
|
||||||
"hls: failed to parse NAL bytes");
|
|
||||||
return NGX_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
nal_bytes = rc;
|
|
||||||
aud_sent = 0;
|
aud_sent = 0;
|
||||||
sps_pps_sent = 0;
|
sps_pps_sent = 0;
|
||||||
|
|
||||||
|
|
63
ngx_rtmp_bitop.c
Normal file
63
ngx_rtmp_bitop.c
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Roman Arutyunyan
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
#include "ngx_rtmp_bitop.h"
|
||||||
|
|
||||||
|
|
||||||
|
void
|
||||||
|
ngx_rtmp_bit_init_reader(ngx_rtmp_bit_reader_t *br, u_char *pos, u_char *last)
|
||||||
|
{
|
||||||
|
ngx_memzero(br, sizeof(ngx_rtmp_bit_reader_t));
|
||||||
|
|
||||||
|
br->pos = pos;
|
||||||
|
br->last = last;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
ngx_rtmp_bit_read(ngx_rtmp_bit_reader_t *br, ngx_uint_t n)
|
||||||
|
{
|
||||||
|
uint64_t v;
|
||||||
|
ngx_uint_t d;
|
||||||
|
|
||||||
|
v = 0;
|
||||||
|
|
||||||
|
while (n) {
|
||||||
|
|
||||||
|
if (br->pos >= br->last) {
|
||||||
|
br->err = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
d = (br->offs + n > 8 ? (ngx_uint_t) (8 - br->offs) : n);
|
||||||
|
|
||||||
|
v <<= d;
|
||||||
|
v += (*br->pos >> (8 - br->offs - d)) & ((u_char) 0xff >> (8 - d));
|
||||||
|
|
||||||
|
br->offs += d;
|
||||||
|
n -= d;
|
||||||
|
|
||||||
|
if (br->offs == 8) {
|
||||||
|
br->pos++;
|
||||||
|
br->offs = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br)
|
||||||
|
{
|
||||||
|
ngx_uint_t n;
|
||||||
|
|
||||||
|
for (n = 0; ngx_rtmp_bit_read(br, 1) == 0 && !br->err; n++);
|
||||||
|
|
||||||
|
return ((uint64_t) 1 << n) + ngx_rtmp_bit_read(br, n) - 1;
|
||||||
|
}
|
46
ngx_rtmp_bitop.h
Normal file
46
ngx_rtmp_bitop.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Copyright (C) Roman Arutyunyan
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _NGX_RTMP_BITOP_H_INCLUDED_
|
||||||
|
#define _NGX_RTMP_BITOP_H_INCLUDED_
|
||||||
|
|
||||||
|
|
||||||
|
#include <ngx_config.h>
|
||||||
|
#include <ngx_core.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
u_char *pos;
|
||||||
|
u_char *last;
|
||||||
|
ngx_uint_t offs;
|
||||||
|
ngx_uint_t err;
|
||||||
|
} ngx_rtmp_bit_reader_t;
|
||||||
|
|
||||||
|
|
||||||
|
void ngx_rtmp_bit_init_reader(ngx_rtmp_bit_reader_t *br, u_char *pos,
|
||||||
|
u_char *last);
|
||||||
|
uint64_t ngx_rtmp_bit_read(ngx_rtmp_bit_reader_t *br, ngx_uint_t n);
|
||||||
|
uint64_t ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br);
|
||||||
|
|
||||||
|
|
||||||
|
#define ngx_rtmp_bit_read_err(br) ((br)->err)
|
||||||
|
|
||||||
|
#define ngx_rtmp_bit_read_eof(br) ((br)->pos == (br)->last)
|
||||||
|
|
||||||
|
#define ngx_rtmp_bit_read_8(br) \
|
||||||
|
((uint8_t) ngx_rtmp_bit_read(br, 8))
|
||||||
|
|
||||||
|
#define ngx_rtmp_bit_read_16(br) \
|
||||||
|
((uint16_t) ngx_rtmp_bit_read(br, 16))
|
||||||
|
|
||||||
|
#define ngx_rtmp_bit_read_32(br) \
|
||||||
|
((uint32_t) ngx_rtmp_bit_read(br, 32))
|
||||||
|
|
||||||
|
#define ngx_rtmp_bit_read_64(br) \
|
||||||
|
((uint64_t) ngx_rtmp_read(br, 64))
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* _NGX_RTMP_BITOP_H_INCLUDED_ */
|
|
@ -9,6 +9,7 @@
|
||||||
#include "ngx_rtmp_codec_module.h"
|
#include "ngx_rtmp_codec_module.h"
|
||||||
#include "ngx_rtmp_live_module.h"
|
#include "ngx_rtmp_live_module.h"
|
||||||
#include "ngx_rtmp_cmd_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_OFF 0
|
||||||
|
@ -25,6 +26,14 @@ 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_header_t *h, ngx_chain_t *in);
|
||||||
static ngx_int_t ngx_rtmp_codec_prepare_meta(ngx_rtmp_session_t *s,
|
static ngx_int_t ngx_rtmp_codec_prepare_meta(ngx_rtmp_session_t *s,
|
||||||
uint32_t timestamp);
|
uint32_t timestamp);
|
||||||
|
static void ngx_rtmp_codec_parse_aac_header(ngx_rtmp_session_t *s,
|
||||||
|
ngx_chain_t *in);
|
||||||
|
static void 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 {
|
typedef struct {
|
||||||
|
@ -189,17 +198,9 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
ngx_rtmp_codec_ctx_t *ctx;
|
ngx_rtmp_codec_ctx_t *ctx;
|
||||||
ngx_chain_t **header;
|
ngx_chain_t **header;
|
||||||
uint8_t fmt;
|
uint8_t fmt;
|
||||||
ngx_uint_t idx;
|
|
||||||
u_char *p;
|
|
||||||
static ngx_uint_t sample_rates[] =
|
static ngx_uint_t sample_rates[] =
|
||||||
{ 5512, 11025, 22050, 44100 };
|
{ 5512, 11025, 22050, 44100 };
|
||||||
|
|
||||||
static ngx_uint_t aac_sample_rates[] =
|
|
||||||
{ 96000, 88200, 64000, 48000,
|
|
||||||
44100, 32000, 24000, 22050,
|
|
||||||
16000, 12000, 11025, 8000,
|
|
||||||
7350, 0, 0, 0 };
|
|
||||||
|
|
||||||
if (h->type != NGX_RTMP_MSG_AUDIO && h->type != NGX_RTMP_MSG_VIDEO) {
|
if (h->type != NGX_RTMP_MSG_AUDIO && h->type != NGX_RTMP_MSG_VIDEO) {
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
@ -221,7 +222,7 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
ctx->audio_channels = (fmt & 0x01) + 1;
|
ctx->audio_channels = (fmt & 0x01) + 1;
|
||||||
ctx->sample_size = (fmt & 0x02) ? 2 : 1;
|
ctx->sample_size = (fmt & 0x02) ? 2 : 1;
|
||||||
|
|
||||||
if (ctx->aac_sample_rate == 0) {
|
if (ctx->sample_rate == 0) {
|
||||||
ctx->sample_rate = sample_rates[(fmt & 0x0c) >> 2];
|
ctx->sample_rate = sample_rates[(fmt & 0x0c) >> 2];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
@ -240,74 +241,16 @@ 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);
|
cscf = ngx_rtmp_get_module_srv_conf(s, ngx_rtmp_core_module);
|
||||||
header = NULL;
|
header = NULL;
|
||||||
|
|
||||||
if (h->type == NGX_RTMP_MSG_AUDIO) {
|
if (h->type == NGX_RTMP_MSG_AUDIO) {
|
||||||
if (ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) {
|
if (ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) {
|
||||||
header = &ctx->aac_header;
|
header = &ctx->aac_header;
|
||||||
|
ngx_rtmp_codec_parse_aac_header(s, in);
|
||||||
if (in->buf->last - in->buf->pos > 3) {
|
|
||||||
p = in->buf->pos + 2;
|
|
||||||
|
|
||||||
/* 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
|
|
||||||
var bits: AOT Specific Config
|
|
||||||
*/
|
|
||||||
|
|
||||||
if ((p[0] >> 3) == 0x1f) {
|
|
||||||
idx = (p[1] >> 1) & 0x0f;
|
|
||||||
} else {
|
|
||||||
idx = ((p[0] << 1) & 0x0f) | (p[1] >> 7);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifdef NGX_DEBUG
|
|
||||||
{
|
|
||||||
u_char buf[256], *p, *pp;
|
|
||||||
u_char hex[] = "01234567890abcdef";
|
|
||||||
|
|
||||||
for (pp = buf, p = in->buf->pos;
|
|
||||||
p < in->buf->last && pp < buf + sizeof(buf) - 1;
|
|
||||||
++p)
|
|
||||||
{
|
|
||||||
*pp++ = hex[*p >> 4];
|
|
||||||
*pp++ = hex[*p & 0x0f];
|
|
||||||
}
|
|
||||||
|
|
||||||
*pp = 0;
|
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
|
||||||
"codec: AAC header: %s", buf);
|
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
ctx->aac_sample_rate = aac_sample_rates[idx];
|
|
||||||
ctx->sample_rate = ctx->aac_sample_rate;
|
|
||||||
}
|
|
||||||
|
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
|
||||||
"codec: aac header arrived, sample_rate=%ui",
|
|
||||||
ctx->aac_sample_rate);
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (ctx->video_codec_id == NGX_RTMP_VIDEO_H264) {
|
if (ctx->video_codec_id == NGX_RTMP_VIDEO_H264) {
|
||||||
header = &ctx->avc_header;
|
header = &ctx->avc_header;
|
||||||
|
ngx_rtmp_codec_parse_avc_header(s, in);
|
||||||
if (in->buf->last - in->buf->pos > 8) {
|
|
||||||
p = in->buf->pos;
|
|
||||||
ctx->avc_profile = p[6];
|
|
||||||
ctx->avc_compat = p[7];
|
|
||||||
ctx->avc_level = p[8];
|
|
||||||
}
|
|
||||||
|
|
||||||
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
|
||||||
"codec: avc header arrived, "
|
|
||||||
"profile=%ui, compat=%ui, level=%ui",
|
|
||||||
ctx->avc_profile, ctx->avc_compat, ctx->avc_level);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -325,6 +268,267 @@ ngx_rtmp_codec_av(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
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);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static void
|
||||||
|
ngx_rtmp_codec_parse_avc_header(ngx_rtmp_session_t *s, ngx_chain_t *in)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
ngx_rtmp_bit_init_reader(&br, in->buf->pos, in->buf->last);
|
||||||
|
|
||||||
|
ngx_rtmp_bit_read(&br, 48);
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* nnals */
|
||||||
|
if ((ngx_rtmp_bit_read_8(&br) & 0x1f) == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* nal size */
|
||||||
|
ngx_rtmp_bit_read(&br, 16);
|
||||||
|
|
||||||
|
/* nal type */
|
||||||
|
if (ngx_rtmp_bit_read_8(&br) != 0x67) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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 ? 8 : 12); 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_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_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);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#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) - 1;
|
||||||
|
++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
|
static ngx_int_t
|
||||||
ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
|
ngx_rtmp_codec_reconstruct_meta(ngx_rtmp_session_t *s)
|
||||||
{
|
{
|
||||||
|
|
|
@ -58,10 +58,13 @@ typedef struct {
|
||||||
ngx_uint_t video_codec_id;
|
ngx_uint_t video_codec_id;
|
||||||
ngx_uint_t audio_data_rate;
|
ngx_uint_t audio_data_rate;
|
||||||
ngx_uint_t audio_codec_id;
|
ngx_uint_t audio_codec_id;
|
||||||
ngx_uint_t aac_sample_rate;
|
ngx_uint_t aac_profile;
|
||||||
|
ngx_uint_t aac_chan_conf;
|
||||||
ngx_uint_t avc_profile;
|
ngx_uint_t avc_profile;
|
||||||
ngx_uint_t avc_compat;
|
ngx_uint_t avc_compat;
|
||||||
ngx_uint_t avc_level;
|
ngx_uint_t avc_level;
|
||||||
|
ngx_uint_t avc_nal_bytes;
|
||||||
|
ngx_uint_t avc_ref_frames;
|
||||||
ngx_uint_t sample_rate; /* 5512, 11025, 22050, 44100 */
|
ngx_uint_t sample_rate; /* 5512, 11025, 22050, 44100 */
|
||||||
ngx_uint_t sample_size; /* 1=8bit, 2=16bit */
|
ngx_uint_t sample_size; /* 1=8bit, 2=16bit */
|
||||||
ngx_uint_t audio_channels; /* 1, 2 */
|
ngx_uint_t audio_channels; /* 1, 2 */
|
||||||
|
|
|
@ -362,6 +362,38 @@ ngx_rtmp_stat_client(ngx_http_request_t *r, ngx_chain_t ***lll,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_rtmp_stat_get_aac_profile(ngx_uint_t p) {
|
||||||
|
switch (p) {
|
||||||
|
case 1:
|
||||||
|
return "Main";
|
||||||
|
case 2:
|
||||||
|
return "LC";
|
||||||
|
case 3:
|
||||||
|
return "SSR";
|
||||||
|
case 4:
|
||||||
|
return "LTP";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static char *
|
||||||
|
ngx_rtmp_stat_get_avc_profile(ngx_uint_t p) {
|
||||||
|
switch (p) {
|
||||||
|
case 66:
|
||||||
|
return "Baseline";
|
||||||
|
case 77:
|
||||||
|
return "Main";
|
||||||
|
case 100:
|
||||||
|
return "High";
|
||||||
|
default:
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
|
ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
|
||||||
ngx_rtmp_live_app_conf_t *lacf)
|
ngx_rtmp_live_app_conf_t *lacf)
|
||||||
|
@ -456,28 +488,55 @@ ngx_rtmp_stat_live(ngx_http_request_t *r, ngx_chain_t ***lll,
|
||||||
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||||
"%ui", codec->frame_rate) - buf);
|
"%ui", codec->frame_rate) - buf);
|
||||||
NGX_RTMP_STAT_L("</framerate>");
|
NGX_RTMP_STAT_L("</framerate>");
|
||||||
|
|
||||||
|
NGX_RTMP_STAT_L("<video>");
|
||||||
cname = ngx_rtmp_get_video_codec_name(codec->video_codec_id);
|
cname = ngx_rtmp_get_video_codec_name(codec->video_codec_id);
|
||||||
if (*cname) {
|
if (*cname) {
|
||||||
NGX_RTMP_STAT_L("<video>");
|
NGX_RTMP_STAT_L("<codec>");
|
||||||
NGX_RTMP_STAT_ECS(cname);
|
NGX_RTMP_STAT_ECS(cname);
|
||||||
NGX_RTMP_STAT_L("</video>");
|
NGX_RTMP_STAT_L("</codec>");
|
||||||
}
|
}
|
||||||
cname = ngx_rtmp_get_audio_codec_name(codec->audio_codec_id);
|
if (codec->avc_profile) {
|
||||||
if (*cname) {
|
|
||||||
NGX_RTMP_STAT_L("<audio>");
|
|
||||||
NGX_RTMP_STAT_ECS(cname);
|
|
||||||
NGX_RTMP_STAT_L("</audio>");
|
|
||||||
}
|
|
||||||
if (*codec->profile) {
|
|
||||||
NGX_RTMP_STAT_L("<profile>");
|
NGX_RTMP_STAT_L("<profile>");
|
||||||
NGX_RTMP_STAT_ECS(codec->profile);
|
NGX_RTMP_STAT_CS(
|
||||||
|
ngx_rtmp_stat_get_avc_profile(codec->avc_profile));
|
||||||
NGX_RTMP_STAT_L("</profile>");
|
NGX_RTMP_STAT_L("</profile>");
|
||||||
}
|
}
|
||||||
if (*codec->level) {
|
if (codec->avc_level) {
|
||||||
|
NGX_RTMP_STAT_L("<compat>");
|
||||||
|
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||||
|
"%ui", codec->avc_compat) - buf);
|
||||||
|
NGX_RTMP_STAT_L("</compat>");
|
||||||
|
}
|
||||||
|
if (codec->avc_level) {
|
||||||
NGX_RTMP_STAT_L("<level>");
|
NGX_RTMP_STAT_L("<level>");
|
||||||
NGX_RTMP_STAT_ECS(codec->level);
|
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||||
|
"%ui", codec->avc_level) - buf);
|
||||||
NGX_RTMP_STAT_L("</level>");
|
NGX_RTMP_STAT_L("</level>");
|
||||||
}
|
}
|
||||||
|
NGX_RTMP_STAT_L("</video>");
|
||||||
|
|
||||||
|
NGX_RTMP_STAT_L("<audio>");
|
||||||
|
cname = ngx_rtmp_get_audio_codec_name(codec->audio_codec_id);
|
||||||
|
if (*cname) {
|
||||||
|
NGX_RTMP_STAT_L("<codec>");
|
||||||
|
NGX_RTMP_STAT_ECS(cname);
|
||||||
|
NGX_RTMP_STAT_L("</codec>");
|
||||||
|
}
|
||||||
|
if (codec->aac_profile) {
|
||||||
|
NGX_RTMP_STAT_L("<profile>");
|
||||||
|
NGX_RTMP_STAT_CS(
|
||||||
|
ngx_rtmp_stat_get_aac_profile(codec->aac_profile));
|
||||||
|
NGX_RTMP_STAT_L("</profile>");
|
||||||
|
}
|
||||||
|
if (codec->aac_chan_conf) {
|
||||||
|
NGX_RTMP_STAT_L("<channels>");
|
||||||
|
NGX_RTMP_STAT(buf, ngx_snprintf(buf, sizeof(buf),
|
||||||
|
"%ui", codec->aac_chan_conf) - buf);
|
||||||
|
NGX_RTMP_STAT_L("</channels>");
|
||||||
|
}
|
||||||
|
NGX_RTMP_STAT_L("</audio>");
|
||||||
|
|
||||||
NGX_RTMP_STAT_L("</meta>\r\n");
|
NGX_RTMP_STAT_L("</meta>\r\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
17
stat.xsl
17
stat.xsl
|
@ -170,11 +170,12 @@
|
||||||
</td>
|
</td>
|
||||||
<td align="middle"><xsl:value-of select="meta/framerate"/></td>
|
<td align="middle"><xsl:value-of select="meta/framerate"/></td>
|
||||||
<td>
|
<td>
|
||||||
<xsl:value-of select="meta/video"/>
|
<xsl:value-of select="meta/video/codec"/> <xsl:value-of select="meta/video/profile"/> <xsl:value-of select="meta/video/level"/>
|
||||||
<xsl:apply-templates select="meta/profile"/>
|
</td>
|
||||||
<xsl:apply-templates select="meta/level"/>
|
<td>
|
||||||
|
<xsl:value-of select="meta/audio/codec"/> <xsl:value-of select="meta/audio/profile"/>
|
||||||
|
<xsl:apply-templates select="meta/audio/channels"/>
|
||||||
</td>
|
</td>
|
||||||
<td><xsl:value-of select="meta/audio"/></td>
|
|
||||||
<td><xsl:call-template name="streamstate"/></td>
|
<td><xsl:call-template name="streamstate"/></td>
|
||||||
<td>
|
<td>
|
||||||
<xsl:call-template name="showtime">
|
<xsl:call-template name="showtime">
|
||||||
|
@ -322,12 +323,8 @@
|
||||||
active
|
active
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
<xsl:template match="profile">
|
<xsl:template match="channels">
|
||||||
/ <xsl:value-of select="."/>
|
x<xsl:value-of select="."/>
|
||||||
</xsl:template>
|
|
||||||
|
|
||||||
<xsl:template match="level">
|
|
||||||
/ <xsl:value-of select="."/>
|
|
||||||
</xsl:template>
|
</xsl:template>
|
||||||
|
|
||||||
</xsl:stylesheet>
|
</xsl:stylesheet>
|
||||||
|
|
Loading…
Reference in a new issue