mirror of
https://github.com/zotanmew/nginx-rtmp-module.git
synced 2024-06-29 08:18:58 +02:00
implemented hls key auto-generation
This commit is contained in:
parent
8acacd0d79
commit
53064a48f1
|
@ -34,6 +34,7 @@ static ngx_int_t ngx_rtmp_hls_ensure_directory(ngx_rtmp_session_t *s);
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
|
uint64_t key_id;
|
||||||
double duration;
|
double duration;
|
||||||
unsigned active:1;
|
unsigned active:1;
|
||||||
unsigned discont:1; /* before */
|
unsigned discont:1; /* before */
|
||||||
|
@ -49,22 +50,26 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
unsigned opened:1;
|
unsigned opened:1;
|
||||||
|
|
||||||
ngx_file_t file;
|
ngx_rtmp_mpegts_file_t file;
|
||||||
|
|
||||||
ngx_str_t playlist;
|
ngx_str_t playlist;
|
||||||
ngx_str_t playlist_bak;
|
ngx_str_t playlist_bak;
|
||||||
ngx_str_t var_playlist;
|
ngx_str_t var_playlist;
|
||||||
ngx_str_t var_playlist_bak;
|
ngx_str_t var_playlist_bak;
|
||||||
ngx_str_t stream;
|
ngx_str_t stream;
|
||||||
|
ngx_str_t keyfile;
|
||||||
ngx_str_t name;
|
ngx_str_t name;
|
||||||
|
u_char key[16];
|
||||||
|
|
||||||
uint64_t frag;
|
uint64_t frag;
|
||||||
uint64_t frag_ts;
|
uint64_t frag_ts;
|
||||||
|
uint64_t key_id;
|
||||||
ngx_uint_t nfrags;
|
ngx_uint_t nfrags;
|
||||||
ngx_rtmp_hls_frag_t *frags; /* circular 2 * winfrags + 1 */
|
ngx_rtmp_hls_frag_t *frags; /* circular 2 * winfrags + 1 */
|
||||||
|
|
||||||
ngx_uint_t audio_cc;
|
ngx_uint_t audio_cc;
|
||||||
ngx_uint_t video_cc;
|
ngx_uint_t video_cc;
|
||||||
|
ngx_uint_t key_frags;
|
||||||
|
|
||||||
uint64_t aframe_base;
|
uint64_t aframe_base;
|
||||||
uint64_t aframe_num;
|
uint64_t aframe_num;
|
||||||
|
@ -79,6 +84,7 @@ typedef struct {
|
||||||
typedef struct {
|
typedef struct {
|
||||||
ngx_str_t path;
|
ngx_str_t path;
|
||||||
ngx_msec_t playlen;
|
ngx_msec_t playlen;
|
||||||
|
ngx_int_t frags_per_key;
|
||||||
} ngx_rtmp_hls_cleanup_t;
|
} ngx_rtmp_hls_cleanup_t;
|
||||||
|
|
||||||
|
|
||||||
|
@ -103,6 +109,10 @@ typedef struct {
|
||||||
ngx_array_t *variant;
|
ngx_array_t *variant;
|
||||||
ngx_str_t base_url;
|
ngx_str_t base_url;
|
||||||
ngx_int_t granularity;
|
ngx_int_t granularity;
|
||||||
|
ngx_flag_t keys;
|
||||||
|
ngx_str_t keys_path;
|
||||||
|
ngx_str_t keys_url;
|
||||||
|
ngx_int_t frags_per_key;
|
||||||
} ngx_rtmp_hls_app_conf_t;
|
} ngx_rtmp_hls_app_conf_t;
|
||||||
|
|
||||||
|
|
||||||
|
@ -269,6 +279,34 @@ static ngx_command_t ngx_rtmp_hls_commands[] = {
|
||||||
offsetof(ngx_rtmp_hls_app_conf_t, granularity),
|
offsetof(ngx_rtmp_hls_app_conf_t, granularity),
|
||||||
NULL },
|
NULL },
|
||||||
|
|
||||||
|
{ ngx_string("hls_keys"),
|
||||||
|
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||||
|
ngx_conf_set_flag_slot,
|
||||||
|
NGX_RTMP_APP_CONF_OFFSET,
|
||||||
|
offsetof(ngx_rtmp_hls_app_conf_t, keys),
|
||||||
|
NULL },
|
||||||
|
|
||||||
|
{ ngx_string("hls_keys_path"),
|
||||||
|
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||||
|
ngx_conf_set_str_slot,
|
||||||
|
NGX_RTMP_APP_CONF_OFFSET,
|
||||||
|
offsetof(ngx_rtmp_hls_app_conf_t, keys_path),
|
||||||
|
NULL },
|
||||||
|
|
||||||
|
{ ngx_string("hls_keys_url"),
|
||||||
|
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||||
|
ngx_conf_set_str_slot,
|
||||||
|
NGX_RTMP_APP_CONF_OFFSET,
|
||||||
|
offsetof(ngx_rtmp_hls_app_conf_t, keys_url),
|
||||||
|
NULL },
|
||||||
|
|
||||||
|
{ ngx_string("hls_fragments_per_key"),
|
||||||
|
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
|
||||||
|
ngx_conf_set_num_slot,
|
||||||
|
NGX_RTMP_APP_CONF_OFFSET,
|
||||||
|
offsetof(ngx_rtmp_hls_app_conf_t, frags_per_key),
|
||||||
|
NULL },
|
||||||
|
|
||||||
ngx_null_command
|
ngx_null_command
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -445,14 +483,14 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
|
||||||
{
|
{
|
||||||
static u_char buffer[1024];
|
static u_char buffer[1024];
|
||||||
ngx_fd_t fd;
|
ngx_fd_t fd;
|
||||||
u_char *p;
|
u_char *p, *end;
|
||||||
ngx_rtmp_hls_ctx_t *ctx;
|
ngx_rtmp_hls_ctx_t *ctx;
|
||||||
ssize_t n;
|
ssize_t n;
|
||||||
ngx_rtmp_hls_app_conf_t *hacf;
|
ngx_rtmp_hls_app_conf_t *hacf;
|
||||||
ngx_rtmp_hls_frag_t *f;
|
ngx_rtmp_hls_frag_t *f;
|
||||||
ngx_uint_t i, max_frag;
|
ngx_uint_t i, max_frag;
|
||||||
ngx_str_t name_part;
|
ngx_str_t name_part, key_name_part;
|
||||||
const char *sep;
|
const char *sep, *key_sep;
|
||||||
|
|
||||||
|
|
||||||
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module);
|
||||||
|
@ -477,15 +515,19 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p = ngx_snprintf(buffer, sizeof(buffer),
|
p = buffer;
|
||||||
|
end = p + sizeof(buffer);
|
||||||
|
|
||||||
|
p = ngx_slprintf(buffer, end,
|
||||||
"#EXTM3U\n"
|
"#EXTM3U\n"
|
||||||
"#EXT-X-VERSION:3\n"
|
"#EXT-X-VERSION:3\n"
|
||||||
"#EXT-X-MEDIA-SEQUENCE:%uL\n"
|
"#EXT-X-MEDIA-SEQUENCE:%uL\n"
|
||||||
"#EXT-X-TARGETDURATION:%ui\n"
|
"#EXT-X-TARGETDURATION:%ui\n",
|
||||||
"%s",
|
ctx->frag, max_frag);
|
||||||
ctx->frag, max_frag,
|
|
||||||
hacf->type == NGX_RTMP_HLS_TYPE_EVENT ?
|
if (hacf->type == NGX_RTMP_HLS_TYPE_EVENT) {
|
||||||
"#EXT-X-PLAYLIST-TYPE: EVENT\n" : "");
|
ngx_slprintf(p, end, "#EXT-X-PLAYLIST-TYPE: EVENT\n");
|
||||||
|
}
|
||||||
|
|
||||||
n = ngx_write_fd(fd, buffer, p - buffer);
|
n = ngx_write_fd(fd, buffer, p - buffer);
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
|
@ -497,20 +539,38 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s)
|
||||||
}
|
}
|
||||||
|
|
||||||
sep = hacf->nested ? (hacf->base_url.len ? "/" : "") : "-";
|
sep = hacf->nested ? (hacf->base_url.len ? "/" : "") : "-";
|
||||||
|
key_sep = hacf->nested ? (hacf->keys_url.len ? "/" : "") : "-";
|
||||||
|
|
||||||
name_part.len = 0;
|
name_part.len = 0;
|
||||||
if (!hacf->nested || hacf->base_url.len) {
|
if (!hacf->nested || hacf->base_url.len) {
|
||||||
name_part = ctx->name;
|
name_part = ctx->name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
key_name_part.len = 0;
|
||||||
|
if (!hacf->nested || hacf->keys_url.len) {
|
||||||
|
key_name_part = ctx->name;
|
||||||
|
}
|
||||||
|
|
||||||
for (i = 0; i < ctx->nfrags; i++) {
|
for (i = 0; i < ctx->nfrags; i++) {
|
||||||
f = ngx_rtmp_hls_get_frag(s, i);
|
f = ngx_rtmp_hls_get_frag(s, i);
|
||||||
|
|
||||||
p = ngx_snprintf(buffer, sizeof(buffer),
|
p = buffer;
|
||||||
"%s"
|
end = p + sizeof(buffer);
|
||||||
|
|
||||||
|
if (f->discont) {
|
||||||
|
p = ngx_slprintf(p, end, "#EXT-X-DISCONTINUITY\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hacf->keys && (i == 0 || f->id != f->key_id)) {
|
||||||
|
p = ngx_slprintf(p, end, "#EXT-X-KEY:METHOD=AES-128,"
|
||||||
|
"URI=\"%V%V%s%uL.key\",IV=0x%032XL",
|
||||||
|
&hacf->keys_url, &key_name_part,
|
||||||
|
key_sep, f->key_id);
|
||||||
|
}
|
||||||
|
|
||||||
|
p = ngx_slprintf(p, end,
|
||||||
"#EXTINF:%.3f,\n"
|
"#EXTINF:%.3f,\n"
|
||||||
"%V%V%s%uL.ts\n",
|
"%V%V%s%uL.ts\n",
|
||||||
f->discont ? "#EXT-X-DISCONTINUITY\n" : "",
|
|
||||||
f->duration, &hacf->base_url, &name_part, sep, f->id);
|
f->duration, &hacf->base_url, &name_part, sep, f->id);
|
||||||
|
|
||||||
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
|
@ -762,10 +822,9 @@ ngx_rtmp_hls_close_fragment(ngx_rtmp_session_t *s)
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
"hls: close fragment n=%uL", ctx->frag);
|
"hls: close fragment n=%uL", ctx->frag);
|
||||||
|
|
||||||
ngx_close_file(ctx->file.fd);
|
ngx_rtmp_mpegts_close_file(&ctx->file);
|
||||||
|
|
||||||
ctx->opened = 0;
|
ctx->opened = 0;
|
||||||
ctx->file.fd = NGX_INVALID_FILE;
|
|
||||||
|
|
||||||
ngx_rtmp_hls_next_frag(s);
|
ngx_rtmp_hls_next_frag(s);
|
||||||
|
|
||||||
|
@ -780,6 +839,7 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts,
|
||||||
ngx_int_t discont)
|
ngx_int_t discont)
|
||||||
{
|
{
|
||||||
uint64_t id;
|
uint64_t id;
|
||||||
|
ngx_fd_t fd;
|
||||||
ngx_uint_t g;
|
ngx_uint_t g;
|
||||||
ngx_rtmp_hls_ctx_t *ctx;
|
ngx_rtmp_hls_ctx_t *ctx;
|
||||||
ngx_rtmp_hls_frag_t *f;
|
ngx_rtmp_hls_frag_t *f;
|
||||||
|
@ -803,32 +863,69 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts,
|
||||||
id = (uint64_t) (id / g) * g;
|
id = (uint64_t) (id / g) * g;
|
||||||
}
|
}
|
||||||
|
|
||||||
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.ts", id) = 0;
|
ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.ts%Z", id);
|
||||||
|
|
||||||
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
if (hacf->keys) {
|
||||||
"hls: open fragment file='%s', frag=%uL, n=%ui, time=%uL, "
|
if (ctx->key_frags-- <= 1) {
|
||||||
"discont=%i",
|
ctx->key_frags = hacf->frags_per_key;
|
||||||
ctx->stream.data, ctx->frag, ctx->nfrags, ts, discont);
|
ctx->key_id = id;
|
||||||
|
|
||||||
ngx_memzero(&ctx->file, sizeof(ctx->file));
|
if (RAND_bytes(ctx->key, 16) < 0) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||||
|
"hls: failed to create key");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
ctx->file.log = s->connection->log;
|
ngx_sprintf(ctx->keyfile.data + ctx->keyfile.len, "%uL.ts%Z", id);
|
||||||
|
|
||||||
ngx_str_set(&ctx->file.name, "hls");
|
fd = ngx_open_file(ctx->keyfile.data, NGX_FILE_RDONLY,
|
||||||
|
NGX_FILE_OPEN, 0);
|
||||||
|
|
||||||
ctx->file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_WRONLY,
|
if (fd == NGX_INVALID_FILE) {
|
||||||
NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS);
|
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||||
|
"hls: failed to open key file '%s'",
|
||||||
|
ctx->keyfile.data);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
if (ctx->file.fd == NGX_INVALID_FILE) {
|
if (ngx_write_fd(fd, ctx->key, 16) != 16) {
|
||||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
||||||
"hls: error creating fragment file");
|
"hls: failed to write key file '%s'",
|
||||||
|
ctx->keyfile.data);
|
||||||
|
ngx_close_file(fd);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_close_file(fd);
|
||||||
|
|
||||||
|
} else if (ngx_set_file_time(ctx->keyfile.data, 0, ngx_cached_time->sec)
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
|
ngx_log_error(NGX_LOG_ALERT, s->connection->log, ngx_errno,
|
||||||
|
ngx_set_file_time_n " '%s' failed",
|
||||||
|
ctx->keyfile.data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
|
"hls: open fragment file='%s', keyfile='%s', "
|
||||||
|
"frag=%uL, n=%ui, time=%uL, discont=%i",
|
||||||
|
ctx->stream.data,
|
||||||
|
ctx->keyfile.data ? ctx->keyfile.data : (u_char *) "",
|
||||||
|
ctx->frag, ctx->nfrags, ts, discont);
|
||||||
|
|
||||||
|
if (ngx_rtmp_mpegts_open_file(&ctx->file, ctx->stream.data,
|
||||||
|
s->connection->log)
|
||||||
|
!= NGX_OK)
|
||||||
|
{
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ngx_rtmp_mpegts_write_header(&ctx->file) != NGX_OK) {
|
if (hacf->keys &&
|
||||||
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno,
|
ngx_rtmp_mpegts_init_encryption(&ctx->file, ctx->key, 16, id) != NGX_OK)
|
||||||
"hls: error writing fragment header");
|
{
|
||||||
ngx_close_file(ctx->file.fd);
|
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
|
||||||
|
"hls: failed to initialize hls encryption");
|
||||||
return NGX_ERROR;
|
return NGX_ERROR;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1258,9 +1355,35 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v)
|
||||||
|
|
||||||
*p = 0;
|
*p = 0;
|
||||||
|
|
||||||
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
/* key path */
|
||||||
"hls: playlist='%V' playlist_bak='%V' stream_pattern='%V'",
|
|
||||||
&ctx->playlist, &ctx->playlist_bak, &ctx->stream);
|
if (hacf->keys) {
|
||||||
|
len = hacf->keys_path.len + 1 + ctx->name.len + NGX_INT64_LEN
|
||||||
|
+ sizeof(".key");
|
||||||
|
|
||||||
|
ctx->keyfile.data = ngx_palloc(s->connection->pool, len);
|
||||||
|
if (ctx->keyfile.data == NULL) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
p = ngx_cpymem(ctx->keyfile.data, hacf->keys_path.data,
|
||||||
|
hacf->keys_path.len);
|
||||||
|
|
||||||
|
if (p[-1] != '/') {
|
||||||
|
*p++ = '/';
|
||||||
|
}
|
||||||
|
|
||||||
|
p = ngx_cpymem(p, ctx->name.data, ctx->name.len);
|
||||||
|
*p++ = (hacf->nested ? '/' : '-');
|
||||||
|
|
||||||
|
ctx->keyfile.len = p - ctx->keyfile.data;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
|
||||||
|
"hls: playlist='%V' playlist_bak='%V' "
|
||||||
|
"stream_pattern='%V' keyfile_pattern='%V'",
|
||||||
|
&ctx->playlist, &ctx->playlist_bak,
|
||||||
|
&ctx->stream, &ctx->keyfile);
|
||||||
|
|
||||||
if (hacf->continuous) {
|
if (hacf->continuous) {
|
||||||
ngx_rtmp_hls_restore_stream(s);
|
ngx_rtmp_hls_restore_stream(s);
|
||||||
|
@ -1973,6 +2096,13 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen)
|
||||||
{
|
{
|
||||||
max_age = playlen / 1000;
|
max_age = playlen / 1000;
|
||||||
|
|
||||||
|
} else if (name.len >= 4 && name.data[name.len - 3] == '.' &&
|
||||||
|
name.data[name.len - 2] == 'k' &&
|
||||||
|
name.data[name.len - 2] == 'e' &&
|
||||||
|
name.data[name.len - 1] == 'y')
|
||||||
|
{
|
||||||
|
max_age = playlen / 500;
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0,
|
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0,
|
||||||
"hls: cleanup skip unknown file type '%V'", &name);
|
"hls: cleanup skip unknown file type '%V'", &name);
|
||||||
|
@ -2088,6 +2218,8 @@ ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf)
|
||||||
conf->audio_buffer_size = NGX_CONF_UNSET_SIZE;
|
conf->audio_buffer_size = NGX_CONF_UNSET_SIZE;
|
||||||
conf->cleanup = NGX_CONF_UNSET;
|
conf->cleanup = NGX_CONF_UNSET;
|
||||||
conf->granularity = NGX_CONF_UNSET;
|
conf->granularity = NGX_CONF_UNSET;
|
||||||
|
conf->keys = NGX_CONF_UNSET;
|
||||||
|
conf->frags_per_key = NGX_CONF_UNSET;
|
||||||
|
|
||||||
return conf;
|
return conf;
|
||||||
}
|
}
|
||||||
|
@ -2122,6 +2254,10 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||||
ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1);
|
ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1);
|
||||||
ngx_conf_merge_str_value(conf->base_url, prev->base_url, "");
|
ngx_conf_merge_str_value(conf->base_url, prev->base_url, "");
|
||||||
ngx_conf_merge_value(conf->granularity, prev->granularity, 0);
|
ngx_conf_merge_value(conf->granularity, prev->granularity, 0);
|
||||||
|
ngx_conf_merge_value(conf->keys, prev->keys, 0);
|
||||||
|
ngx_conf_merge_str_value(conf->keys_path, prev->keys_path, "");
|
||||||
|
ngx_conf_merge_str_value(conf->keys_url, prev->keys_url, "");
|
||||||
|
ngx_conf_merge_value(conf->frags_per_key, prev->frags_per_key, 0);
|
||||||
|
|
||||||
if (conf->fraglen) {
|
if (conf->fraglen) {
|
||||||
conf->winfrags = conf->playlen / conf->fraglen;
|
conf->winfrags = conf->playlen / conf->fraglen;
|
||||||
|
@ -2160,6 +2296,42 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (conf->keys_path.len == 0) {
|
||||||
|
conf->keys_path = conf->path;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conf->keys && conf->keys_path.len && conf->cleanup &&
|
||||||
|
ngx_strcmp(conf->keys_path.data, conf->path.data) == 0 &&
|
||||||
|
conf->type != NGX_RTMP_HLS_TYPE_EVENT)
|
||||||
|
{
|
||||||
|
if (conf->keys_path.data[conf->path.len - 1] == '/') {
|
||||||
|
conf->keys_path.len--;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup = ngx_pcalloc(cf->pool, sizeof(*cleanup));
|
||||||
|
if (cleanup == NULL) {
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup->path = conf->keys_path;
|
||||||
|
cleanup->playlen = conf->playlen;
|
||||||
|
|
||||||
|
conf->slot = ngx_pcalloc(cf->pool, sizeof(*conf->slot));
|
||||||
|
if (conf->slot == NULL) {
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf->slot->manager = ngx_rtmp_hls_cleanup;
|
||||||
|
conf->slot->name = conf->path;
|
||||||
|
conf->slot->data = cleanup;
|
||||||
|
conf->slot->conf_file = cf->conf_file->file.name.data;
|
||||||
|
conf->slot->line = cf->conf_file->line;
|
||||||
|
|
||||||
|
if (ngx_add_path(cf, &conf->slot) != NGX_OK) {
|
||||||
|
return NGX_CONF_ERROR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ngx_conf_merge_str_value(conf->path, prev->path, "");
|
ngx_conf_merge_str_value(conf->path, prev->path, "");
|
||||||
|
|
||||||
return NGX_CONF_OK;
|
return NGX_CONF_OK;
|
||||||
|
|
|
@ -75,15 +75,48 @@ static u_char ngx_rtmp_mpegts_header[] = {
|
||||||
#define NGX_RTMP_HLS_DELAY 63000
|
#define NGX_RTMP_HLS_DELAY 63000
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
static ngx_int_t
|
||||||
ngx_rtmp_mpegts_write_header(ngx_file_t *file)
|
ngx_rtmp_mpegts_write_file(ngx_rtmp_mpegts_file_t *file, void *data,
|
||||||
|
size_t size)
|
||||||
{
|
{
|
||||||
ssize_t rc;
|
u_char buf[16];
|
||||||
|
ssize_t rc;
|
||||||
|
|
||||||
rc = ngx_write_file(file, ngx_rtmp_mpegts_header,
|
if (!file->encrypt) {
|
||||||
sizeof(ngx_rtmp_mpegts_header), 0);
|
rc = ngx_write_file(&file->file, data, size, file->file.offset);
|
||||||
|
return rc > 0 ? NGX_OK : rc;
|
||||||
|
}
|
||||||
|
|
||||||
return rc > 0 ? NGX_OK : rc;
|
/* encrypt */
|
||||||
|
|
||||||
|
for ( ;; ) {
|
||||||
|
if (file->size + size < 16) {
|
||||||
|
ngx_memcpy(file->buf + file->size, data, size);
|
||||||
|
file->size += size;
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_memcpy(file->buf + file->size, data, 16 - file->size);
|
||||||
|
|
||||||
|
AES_cbc_encrypt(file->buf, buf, 16, &file->key, file->iv, AES_ENCRYPT);
|
||||||
|
|
||||||
|
rc = ngx_write_file(&file->file, buf, 16, file->file.offset);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
data = (u_char *) data + (16 - file->size);
|
||||||
|
size -= 16 - file->size;
|
||||||
|
file->size = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static ngx_int_t
|
||||||
|
ngx_rtmp_mpegts_write_header(ngx_rtmp_mpegts_file_t *file)
|
||||||
|
{
|
||||||
|
return ngx_rtmp_mpegts_write_file(file, ngx_rtmp_mpegts_header,
|
||||||
|
sizeof(ngx_rtmp_mpegts_header));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,14 +155,14 @@ ngx_rtmp_mpegts_write_pts(u_char *p, ngx_uint_t fb, uint64_t pts)
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t
|
ngx_int_t
|
||||||
ngx_rtmp_mpegts_write_frame(ngx_file_t *file, ngx_rtmp_mpegts_frame_t *f,
|
ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file,
|
||||||
ngx_buf_t *b)
|
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b)
|
||||||
{
|
{
|
||||||
ngx_uint_t pes_size, header_size, body_size, in_size, stuff_size, flags;
|
ngx_uint_t pes_size, header_size, body_size, in_size, stuff_size, flags;
|
||||||
u_char packet[188], *p, *base;
|
u_char packet[188], *p, *base;
|
||||||
ngx_int_t first, rc;
|
ngx_int_t first, rc;
|
||||||
|
|
||||||
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, file->log, 0,
|
ngx_log_debug6(NGX_LOG_DEBUG_HTTP, file->file.log, 0,
|
||||||
"mpegts: pid=%ui, sid=%ui, pts=%uL, "
|
"mpegts: pid=%ui, sid=%ui, pts=%uL, "
|
||||||
"dts=%uL, key=%ui, size=%ui",
|
"dts=%uL, key=%ui, size=%ui",
|
||||||
f->pid, f->sid, f->pts, f->dts,
|
f->pid, f->sid, f->pts, f->dts,
|
||||||
|
@ -238,11 +271,83 @@ ngx_rtmp_mpegts_write_frame(ngx_file_t *file, ngx_rtmp_mpegts_frame_t *f,
|
||||||
b->pos = b->last;
|
b->pos = b->last;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = ngx_write_file(file, packet, sizeof(packet), file->offset);
|
rc = ngx_rtmp_mpegts_write_file(file, packet, sizeof(packet));
|
||||||
if (rc < 0) {
|
if (rc != NGX_OK) {
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return NGX_OK;
|
return NGX_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file,
|
||||||
|
u_char *key, size_t key_len, uint64_t iv)
|
||||||
|
{
|
||||||
|
if (AES_set_encrypt_key(key, key_len * 8, &file->key)) {
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_memzero(file->iv, 8);
|
||||||
|
ngx_memcpy(file->iv + 8, &iv, 8);
|
||||||
|
|
||||||
|
file->encrypt = 1;
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
|
||||||
|
ngx_log_t *log)
|
||||||
|
{
|
||||||
|
ngx_memzero(&file->file, sizeof(ngx_file_t));
|
||||||
|
|
||||||
|
ngx_str_set(&file->file.name, "hls");
|
||||||
|
|
||||||
|
file->file.log = log;
|
||||||
|
|
||||||
|
file->file.fd = ngx_open_file(path, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE,
|
||||||
|
NGX_FILE_DEFAULT_ACCESS);
|
||||||
|
|
||||||
|
if (file->file.fd == NGX_INVALID_FILE) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
|
||||||
|
"hls: error creating fragment file");
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
file->size = 0;
|
||||||
|
|
||||||
|
if (ngx_rtmp_mpegts_write_header(file) != NGX_OK) {
|
||||||
|
ngx_log_error(NGX_LOG_ERR, log, ngx_errno,
|
||||||
|
"hls: error writing fragment header");
|
||||||
|
ngx_close_file(file->file.fd);
|
||||||
|
return NGX_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ngx_int_t
|
||||||
|
ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file)
|
||||||
|
{
|
||||||
|
u_char buf[16];
|
||||||
|
ssize_t rc;
|
||||||
|
|
||||||
|
if (file->encrypt) {
|
||||||
|
ngx_memset(file->buf + file->size, 16 - file->size, 16 - file->size);
|
||||||
|
|
||||||
|
AES_cbc_encrypt(file->buf, buf, 16, &file->key, file->iv, AES_ENCRYPT);
|
||||||
|
|
||||||
|
rc = ngx_write_file(&file->file, buf, 16, file->file.offset);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ngx_close_file(file->file.fd);
|
||||||
|
|
||||||
|
return NGX_OK;
|
||||||
|
}
|
||||||
|
|
|
@ -10,6 +10,17 @@
|
||||||
|
|
||||||
#include <ngx_config.h>
|
#include <ngx_config.h>
|
||||||
#include <ngx_core.h>
|
#include <ngx_core.h>
|
||||||
|
#include <openssl/aes.h>
|
||||||
|
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
ngx_file_t file;
|
||||||
|
unsigned encrypt:1;
|
||||||
|
unsigned size:4;
|
||||||
|
u_char buf[16];
|
||||||
|
u_char iv[16];
|
||||||
|
AES_KEY key;
|
||||||
|
} ngx_rtmp_mpegts_file_t;
|
||||||
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
|
@ -22,9 +33,13 @@ typedef struct {
|
||||||
} ngx_rtmp_mpegts_frame_t;
|
} ngx_rtmp_mpegts_frame_t;
|
||||||
|
|
||||||
|
|
||||||
ngx_int_t ngx_rtmp_mpegts_write_header(ngx_file_t *file);
|
ngx_int_t ngx_rtmp_mpegts_init_encryption(ngx_rtmp_mpegts_file_t *file,
|
||||||
ngx_int_t ngx_rtmp_mpegts_write_frame(ngx_file_t *file,
|
u_char *key, size_t key_len, uint64_t iv);
|
||||||
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b);
|
ngx_int_t ngx_rtmp_mpegts_open_file(ngx_rtmp_mpegts_file_t *file, u_char *path,
|
||||||
|
ngx_log_t *log);
|
||||||
|
ngx_int_t ngx_rtmp_mpegts_close_file(ngx_rtmp_mpegts_file_t *file);
|
||||||
|
ngx_int_t ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file,
|
||||||
|
ngx_rtmp_mpegts_frame_t *f, ngx_buf_t *b);
|
||||||
|
|
||||||
|
|
||||||
#endif /* _NGX_RTMP_MPEGTS_H_INCLUDED_ */
|
#endif /* _NGX_RTMP_MPEGTS_H_INCLUDED_ */
|
||||||
|
|
Loading…
Reference in a new issue