From ab8ef3a0a869e8c6e38911038270d1d554d8ea12 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 16 Dec 2016 19:58:01 +0000 Subject: [PATCH 001/113] First merge of readd dash variant --- config | 1 + dash/ngx_rtmp_dash_module.c | 112 ++++++++++++++++++++++++++++++++- dash/ngx_rtmp_dash_templates.h | 108 +++++++++++++++++++++++++++++++ 3 files changed, 220 insertions(+), 1 deletion(-) create mode 100644 dash/ngx_rtmp_dash_templates.h diff --git a/config b/config index 3e5a30b53..b622a549e 100644 --- a/config +++ b/config @@ -43,6 +43,7 @@ RTMP_DEPS=" \ $ngx_addon_dir/ngx_rtmp_proxy_protocol.h \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.h \ $ngx_addon_dir/dash/ngx_rtmp_mp4.h \ + $ngx_addon_dir/dash/ngx_rtmp_dash_templates.h \ " RTMP_CORE_SRCS=" \ $ngx_addon_dir/ngx_rtmp.c \ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 13d6572d6..ea294949b 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -15,6 +15,8 @@ static ngx_rtmp_stream_eof_pt next_stream_eof; static ngx_rtmp_playlist_pt next_playlist; +static char * ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf); static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, @@ -50,9 +52,17 @@ typedef struct { } ngx_rtmp_dash_track_t; +typedef struct { + ngx_str_t suffix; + ngx_array_t args; +} ngx_rtmp_dash_variant_t; + + typedef struct { ngx_str_t playlist; ngx_str_t playlist_bak; + ngx_str_t var_playlist; + ngx_str_t var_playlist_bak; ngx_str_t name; ngx_str_t stream; ngx_time_t start_time; @@ -72,6 +82,7 @@ typedef struct { ngx_rtmp_dash_track_t audio; ngx_rtmp_dash_track_t video; + ngx_rtmp_dash_variant_t *var; } ngx_rtmp_dash_ctx_t; @@ -108,6 +119,7 @@ typedef struct { ngx_uint_t winfrags; ngx_flag_t cleanup; ngx_path_t *slot; + ngx_array_t *variant; } ngx_rtmp_dash_app_conf_t; @@ -169,6 +181,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, clock_helper_uri), NULL }, + { ngx_string("dash_variant"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE, + ngx_rtmp_dash_variant, + NGX_RTMP_APP_CONF_OFFSET, + 0, + NULL }, + ngx_null_command }; @@ -1022,11 +1041,13 @@ ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s) static ngx_int_t ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { - u_char *p; + u_char *p, *pp; size_t len; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_dash_frag_t *f; ngx_rtmp_dash_app_conf_t *dacf; + ngx_rtmp_dash_variant_t *var; + ngx_uint_t n; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { @@ -1113,6 +1134,44 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1); ctx->stream.data[ctx->stream.len - 1] = (dacf->nested ? '/' : '-'); + if (dacf->variant) { + var = dacf->variant->elts; + for (n = 0; n < dacf->variant->nelts; n++, var++) { + if (ctx->name.len > var->suffix.len && + ngx_memcmp(var->suffix.data, + ctx->name.data + ctx->name.len - var->suffix.len, + var->suffix.len) + == 0) + { + + ctx->var = var; + + len = (size_t) (p - ctx->playlist.data); + + ctx->var_playlist.len = len - var->suffix.len + sizeof(".mpd") + -1; + ctx->var_playlist.data = ngx_palloc(s->connection->pool, + ctx->var_playlist.len + 1); + pp = ngx_cpymem(ctx->var_playlist.data, + ctx->playlist.data, len - var->suffix.len); + pp = ngx_cpymem(pp, ".mpd", sizeof(".mpd") - 1); + *pp = 0; + + ctx->var_playlist_bak.len = ctx->var_playlist.len + + sizeof(".bak") - 1; + ctx->var_playlist_bak.data = ngx_palloc(s->connection->pool, + ctx->var_playlist_bak.len + 1); + pp = ngx_cpymem(ctx->var_playlist_bak.data, + ctx->var_playlist.data, + ctx->var_playlist.len); + pp = ngx_cpymem(pp, ".bak", sizeof(".bak") - 1); + *pp = 0; + + break; + } + } + } + if (dacf->nested) { p = ngx_cpymem(p, "/index.mpd", sizeof("/index.mpd") - 1); } else { @@ -1641,6 +1700,57 @@ ngx_rtmp_dash_cleanup(void *data) #endif } +static char * +ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_rtmp_dash_app_conf_t *dacf = conf; + + ngx_str_t *value, *arg; + ngx_uint_t n; + ngx_rtmp_dash_variant_t *var; + + value = cf->args->elts; + + if (dacf->variant == NULL) { + dacf->variant = ngx_array_create(cf->pool, 1, + sizeof(ngx_rtmp_dash_variant_t)); + if (dacf->variant == NULL) { + return NGX_CONF_ERROR; + } + } + + var = ngx_array_push(dacf->variant); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(var, sizeof(ngx_rtmp_dash_variant_t)); + + var->suffix = value[1]; + + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } + + if (ngx_array_init(&var->args, cf->pool, cf->args->nelts - 2, + sizeof(ngx_str_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + arg = ngx_array_push_n(&var->args, cf->args->nelts - 2); + if (arg == NULL) { + return NGX_CONF_ERROR; + } + + for (n = 2; n < cf->args->nelts; n++) { + *arg++ = value[n]; + } + + return NGX_CONF_OK; +} + static ngx_int_t ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v) { diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h new file mode 100644 index 000000000..e75ed5f98 --- /dev/null +++ b/dash/ngx_rtmp_dash_templates.h @@ -0,0 +1,108 @@ +#ifndef _NGX_RTMP_DASH_TEMPLATES +#define _NGX_RTMP_DASH_TEMPLATES + +#define NGX_RTMP_DASH_MANIFEST_HEADER \ + "\n" \ + "\n" + + +#define NGX_RTMP_DASH_MANIFEST_PERIOD \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ + " \n" \ + " \n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \ + " \n" \ + " \n" \ + " \n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_TIME \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_AUDIO \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER \ + " \n" \ + " \n" \ + " \n" \ + " \n" + + +#define NGX_RTMP_DASH_PERIOD_FOOTER \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_CLOCK \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_FOOTER \ + "\n" + + +#endif From 82b86130f4da1ea6ab357431bdc1000b1d593164 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 18 Dec 2016 11:17:04 +0000 Subject: [PATCH 002/113] Revert to simple template --- dash/ngx_rtmp_dash_templates.h | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index e75ed5f98..816cdf3c1 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -1,6 +1,7 @@ #ifndef _NGX_RTMP_DASH_TEMPLATES #define _NGX_RTMP_DASH_TEMPLATES + #define NGX_RTMP_DASH_MANIFEST_HEADER \ "\n" \ "\n" -#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ +#define NGX_RTMP_DASH_MANIFEST_VIDEO \ " \n" - - -#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ + " par=\"%ui:%ui\">\n" \ " \n" \ - " \n" + " media=\"%V%s$Time$.m4v\"\n" \ + " initialization=\"%V%sinit.m4v\">\n" \ + " \n" -#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \ +#define NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER \ " \n" \ " \n" \ " \n" \ From b716c44cf087caf2511daf1ef11c316e01e8a598 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 18 Dec 2016 11:20:21 +0000 Subject: [PATCH 003/113] Working version with split template in .h --- dash/ngx_rtmp_dash_module.c | 110 ++---------------------------------- 1 file changed, 5 insertions(+), 105 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index ea294949b..203c5e82a 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -6,6 +6,7 @@ #include #include "ngx_rtmp_live_module.h" #include "ngx_rtmp_mp4.h" +#include "ngx_rtmp_dash_templates.h" static ngx_rtmp_publish_pt next_publish; @@ -331,111 +332,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } - -#define NGX_RTMP_DASH_MANIFEST_HEADER \ - "\n" \ - "\n" - -#define NGX_RTMP_DASH_MANIFEST_PERIOD \ - " \n" - - -#define NGX_RTMP_DASH_MANIFEST_VIDEO \ - " \n" \ - " \n" \ - " \n" \ - " \n" - - -#define NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER \ - " \n" \ - " \n" \ - " \n" \ - " \n" - - -#define NGX_RTMP_DASH_MANIFEST_TIME \ - " \n" - - -#define NGX_RTMP_DASH_MANIFEST_AUDIO \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" - - -#define NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER \ - " \n" \ - " \n" \ - " \n" \ - " \n" - - -#define NGX_RTMP_DASH_PERIOD_FOOTER \ - " \n" - - -#define NGX_RTMP_DASH_MANIFEST_CLOCK \ - " \n" - - -#define NGX_RTMP_DASH_MANIFEST_FOOTER \ - "\n" - - -/** - * Availability time must be equal stream start time - * Cos segments time counting from it - */ + /** + * Availability time must be equal stream start time + * Cos segments time counting from it + */ ngx_libc_gmtime(ctx->start_time.sec, &tm); *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", tm.tm_year + 1900, tm.tm_mon + 1, From fe33879ca122e6a8f08113986f6038e356ebf5e6 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 18 Dec 2016 11:46:25 +0000 Subject: [PATCH 004/113] Splitting adpatationset and representation --- dash/ngx_rtmp_dash_module.c | 6 ++++-- dash/ngx_rtmp_dash_templates.h | 7 +++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 203c5e82a..35e57b7a6 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -451,11 +451,13 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) par_x = codec_ctx->width / gcd; par_y = codec_ctx->height / gcd; - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_VIDEO, + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO, codec_ctx->width, codec_ctx->height, frame_rate, - par_x, par_y, + par_x, par_y); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, codec_ctx->avc_profile, codec_ctx->avc_compat, diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 816cdf3c1..bdae8cf51 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -23,7 +23,7 @@ " \n" -#define NGX_RTMP_DASH_MANIFEST_VIDEO \ +#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ " \n" \ + " par=\"%ui:%ui\">\n" + + +#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ " Date: Sun, 18 Dec 2016 12:36:35 +0000 Subject: [PATCH 005/113] Video footer OK --- dash/ngx_rtmp_dash_module.c | 4 +++- dash/ngx_rtmp_dash_templates.h | 7 +++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 35e57b7a6..86ad2efe9 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -475,7 +475,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) f->timestamp, f->duration); } - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); } diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index bdae8cf51..ad5e4b80a 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -52,10 +52,13 @@ " \n" -#define NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER \ +#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \ " \n" \ " \n" \ - " \n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER \ " \n" From 427bc977d7f5e6cae6261c58492df8ec7592e4b8 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 18 Dec 2016 12:51:32 +0000 Subject: [PATCH 006/113] Split Adpatationset and representation for audio too --- dash/ngx_rtmp_dash_module.c | 8 ++++++-- dash/ngx_rtmp_dash_templates.h | 14 ++++++++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 86ad2efe9..48ede0e9a 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -483,7 +483,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (ctx->has_audio) { - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_AUDIO, + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", @@ -498,7 +500,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) f->timestamp, f->duration); } - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); } diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index ad5e4b80a..62d728079 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -66,7 +66,7 @@ " \n" -#define NGX_RTMP_DASH_MANIFEST_AUDIO \ +#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO \ " \n" \ + " value=\"1\"/>\n" + + +#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO \ " \n" -#define NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER \ +#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER \ " \n" \ " \n" \ - " \n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER \ " \n" From 69710ee0d0efbce6b58edcf43ab2a883e2626942 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 18 Dec 2016 16:32:00 +0000 Subject: [PATCH 007/113] write an quasi empty multivariant manifest --- dash/ngx_rtmp_dash_module.c | 200 ++++++++++++++++++++++++++++++++++++ 1 file changed, 200 insertions(+) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 48ede0e9a..6bc45ff88 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -283,6 +283,202 @@ ngx_rtmp_dash_gcd(ngx_uint_t m, ngx_uint_t n) } +static ngx_int_t +ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) +{ + //char *sep; + u_char *p, *last; + ssize_t n; + ngx_fd_t fd; + struct tm tm; + //ngx_str_t noname, *name; + //ngx_uint_t i, frame_rate_num, frame_rate_denom; + ngx_uint_t i; + ngx_uint_t depth_msec, depth_sec; + ngx_uint_t update_period, update_period_msec; + ngx_uint_t buffer_time, buffer_time_msec; + ngx_uint_t presentation_delay, presentation_delay_msec; + //ngx_uint_t gcd, par_x, par_y; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_frag_t *f; + ngx_rtmp_dash_app_conf_t *dacf; + + ngx_rtmp_playlist_t v; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; + //static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { + return NGX_ERROR; + } + + fd = ngx_open_file(ctx->var_playlist_bak.data, NGX_FILE_WRONLY, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: open failed: '%V'", &ctx->var_playlist_bak); + return NGX_ERROR; + } + + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: variant %s", ctx->var_playlist_bak.data); + + /** + * Availability time must be equal stream start time + * Cos segments time counting from it + */ + ngx_libc_gmtime(ctx->start_time.sec, &tm); + *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", + tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec + ) = 0; + + /* Stream publish time */ + *ngx_sprintf(publish_time, "%s", avaliable_time) = 0; + + depth_sec = (ngx_uint_t) ( + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration - + ngx_rtmp_dash_get_frag(s, 0)->timestamp); + + depth_msec = depth_sec % 1000; + depth_sec -= depth_msec; + depth_sec /= 1000; + + ngx_libc_gmtime(depth_sec, &tm); + + *ngx_sprintf(buffer_depth, "P%dY%02dM%02dDT%dH%02dM%02d.%03dS", + tm.tm_year - 70, tm.tm_mon, + tm.tm_mday - 1, tm.tm_hour, + tm.tm_min, tm.tm_sec, + depth_msec) = 0; + + last = buffer + sizeof(buffer); + + /** + * Calculate playlist minimal update period + * This should be more than biggest segment duration + * Cos segments rounded by keyframe/GOP. + * And that time not always equals to fragment length. + */ + update_period = dacf->fraglen; + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + if (f->duration > update_period) { + update_period = f->duration; + } + } + + // Reasonable delay for streaming + presentation_delay = update_period * 2 + 1000; + presentation_delay_msec = presentation_delay % 1000; + presentation_delay -= presentation_delay_msec; + presentation_delay /= 1000; + + // Calculate msec part and seconds + update_period_msec = update_period % 1000; + update_period -= update_period_msec; + update_period /= 1000; + + // Buffer length by default fragment length + buffer_time = dacf->fraglen; + buffer_time_msec = buffer_time % 1000; + buffer_time -= buffer_time_msec; + buffer_time /= 1000; + + // Fill DASH header + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, + // availabilityStartTime + avaliable_time, + // publishTime + publish_time, + // minimumUpdatePeriod + update_period, update_period_msec, + // minBufferTime + buffer_time, buffer_time_msec, + // timeShiftBufferDepth + buffer_depth, + // suggestedPresentationDelay + presentation_delay, presentation_delay_msec + ); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_PERIOD); + + n = ngx_write_fd(fd, buffer, p - buffer); + + //ngx_str_null(&noname); + + //name = (dacf->nested ? &noname : &ctx->name); + //sep = (dacf->nested ? "" : "-"); + + // + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); + + /* UTCTiming value */ + switch (dacf->clock_compensation) { + case NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "ntp", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "http-head", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "http-iso", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + } + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); + + if (n < 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: write failed: '%V'", &ctx->var_playlist_bak); + ngx_close_file(fd); + return NGX_ERROR; + } + + ngx_close_file(fd); + + if (ngx_rtmp_dash_rename_file(ctx->var_playlist_bak.data, ctx->var_playlist.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%V'->'%V'", + &ctx->var_playlist_bak, &ctx->var_playlist); + return NGX_ERROR; + } + + ngx_memzero(&v, sizeof(v)); + ngx_str_set(&(v.module), "dash"); + v.playlist.data = ctx->playlist.data; + v.playlist.len = ctx->playlist.len; + return next_playlist(s, &v); +} + + static ngx_int_t ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { @@ -556,6 +752,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } + if (ctx->var) { + return ngx_rtmp_dash_write_variant_playlist(s); + } + ngx_memzero(&v, sizeof(v)); ngx_str_set(&(v.module), "dash"); v.playlist.data = ctx->playlist.data; From 8af33adc0c850fa994244c8841d0f1aded06cfbd Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 18 Dec 2016 18:58:51 +0000 Subject: [PATCH 008/113] Add varname, ctxname - suffix --- dash/ngx_rtmp_dash_module.c | 77 ++++++++++++++++++++++++++++++++++--- 1 file changed, 71 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 6bc45ff88..eb77683ca 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -65,6 +65,7 @@ typedef struct { ngx_str_t var_playlist; ngx_str_t var_playlist_bak; ngx_str_t name; + ngx_str_t varname; ngx_str_t stream; ngx_time_t start_time; @@ -292,17 +293,17 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ngx_fd_t fd; struct tm tm; //ngx_str_t noname, *name; - //ngx_uint_t i, frame_rate_num, frame_rate_denom; - ngx_uint_t i; + ngx_uint_t i, j, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; ngx_uint_t buffer_time, buffer_time_msec; ngx_uint_t presentation_delay, presentation_delay_msec; - //ngx_uint_t gcd, par_x, par_y; + ngx_uint_t gcd, par_x, par_y; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_dash_frag_t *f; ngx_rtmp_dash_app_conf_t *dacf; + ngx_rtmp_dash_variant_t *var; ngx_rtmp_playlist_t v; @@ -310,7 +311,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; - //static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; + static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -329,8 +330,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: variant %s", ctx->var_playlist_bak.data); - /** * Availability time must be equal stream start time * Cos segments time counting from it @@ -420,7 +419,64 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) //name = (dacf->nested ? &noname : &ctx->name); //sep = (dacf->nested ? "" : "-"); + var = dacf->variant->elts; + + if (ctx->has_video) { + frame_rate_num = (ngx_uint_t) (codec_ctx->frame_rate * 1000.); + + if (frame_rate_num % 1000 == 0) { + *ngx_sprintf(frame_rate, "%ui", frame_rate_num / 1000) = 0; + } else { + frame_rate_denom = 1000; + switch (frame_rate_num) { + case 23976: + frame_rate_num = 24000; + frame_rate_denom = 1001; + break; + case 29970: + frame_rate_num = 30000; + frame_rate_denom = 1001; + break; + case 59940: + frame_rate_num = 60000; + frame_rate_denom = 1001; + break; + } + *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; + } + + // perhaps max width ? + gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height); + par_x = codec_ctx->width / gcd; + par_y = codec_ctx->height / gcd; + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO, + codec_ctx->width, + codec_ctx->height, + frame_rate, + par_x, par_y); + + for (j = 0; j < dacf->variant->nelts; j++, var++) + { + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: variant %s %s", var->suffix.data, ctx->varname.data); + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + f->timestamp, f->duration); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); + + n = ngx_write_fd(fd, buffer, p - buffer); + } + + //audio here // p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); @@ -1251,6 +1307,15 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) var->suffix.len) == 0) { + len = (size_t) (ctx->name.len - var->suffix.len); + + ctx->varname.len = len; + ctx->varname.data = ngx_palloc(s->connection->pool, + ctx->varname.len + 1); + pp = ngx_cpymem(ctx->varname.data, + ctx->name.data, len); + + *pp = 0; ctx->var = var; From a39c7fd9a0beef6b9132f3aac5c770651d4d65a3 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 21 Dec 2016 16:30:15 +0000 Subject: [PATCH 009/113] Video variant working with argument in the configuration file --- dash/ngx_rtmp_dash_module.c | 27 ++++++++++++++++++++++----- dash/ngx_rtmp_dash_templates.h | 25 +++++++++++++++++++++++++ 2 files changed, 47 insertions(+), 5 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index eb77683ca..546c9ce79 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -287,13 +287,13 @@ ngx_rtmp_dash_gcd(ngx_uint_t m, ngx_uint_t n) static ngx_int_t ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) { - //char *sep; + char *sep; u_char *p, *last; ssize_t n; ngx_fd_t fd; struct tm tm; //ngx_str_t noname, *name; - ngx_uint_t i, j, frame_rate_num, frame_rate_denom; + ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; ngx_uint_t buffer_time, buffer_time_msec; @@ -304,6 +304,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ngx_rtmp_dash_frag_t *f; ngx_rtmp_dash_app_conf_t *dacf; ngx_rtmp_dash_variant_t *var; + ngx_str_t *arg; ngx_rtmp_playlist_t v; @@ -418,7 +419,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) //ngx_str_null(&noname); //name = (dacf->nested ? &noname : &ctx->name); - //sep = (dacf->nested ? "" : "-"); + sep = (dacf->nested ? "" : "-"); var = dacf->variant->elts; if (ctx->has_video) { @@ -446,7 +447,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; } - // perhaps max width ? + // perhaps max width/height ? gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height); par_x = codec_ctx->width / gcd; par_y = codec_ctx->height / gcd; @@ -459,9 +460,25 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) for (j = 0; j < dacf->variant->nelts; j++, var++) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: variant %s %s", var->suffix.data, ctx->varname.data); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, + &ctx->varname, &var->suffix, + codec_ctx->avc_profile, + codec_ctx->avc_compat, + codec_ctx->avc_level); + + arg = var->args.elts; + for (k = 0; k < var->args.nelts; k++, arg++) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO, + &ctx->varname, &var->suffix, sep, + &ctx->varname, &var->suffix, sep); + for (i = 0; i < ctx->nfrags; i++) { f = ngx_rtmp_dash_get_frag(s, i); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 62d728079..1ac99cdab 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -52,6 +52,31 @@ " \n" +#define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO \ + " \n" \ + " \n" + + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \ " \n" \ " \n" \ From 7a55c6432b68b648c7289494a574ca8da5f9e2d8 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 21 Dec 2016 17:20:42 +0000 Subject: [PATCH 010/113] Add audio should work choosing etheir track --- dash/ngx_rtmp_dash_module.c | 36 ++++++++++++++++++++++++++++-------- 1 file changed, 28 insertions(+), 8 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 546c9ce79..b8796a162 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -292,7 +292,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ssize_t n; ngx_fd_t fd; struct tm tm; - //ngx_str_t noname, *name; + ngx_str_t noname, *name; ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; @@ -416,9 +416,9 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) n = ngx_write_fd(fd, buffer, p - buffer); - //ngx_str_null(&noname); + ngx_str_null(&noname); - //name = (dacf->nested ? &noname : &ctx->name); + name = (dacf->nested ? &noname : &ctx->name); sep = (dacf->nested ? "" : "-"); var = dacf->variant->elts; @@ -447,7 +447,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; } - // perhaps max width/height ? gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height); par_x = codec_ctx->width / gcd; par_y = codec_ctx->height / gcd; @@ -460,8 +459,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) for (j = 0; j < dacf->variant->nelts; j++, var++) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: variant %s %s", var->suffix.data, ctx->varname.data); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, &ctx->varname, &var->suffix, codec_ctx->avc_profile, @@ -493,8 +490,31 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) n = ngx_write_fd(fd, buffer, p - buffer); } - //audio here - // + if (ctx->has_audio) { + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, + &ctx->name, + codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? + (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", + codec_ctx->sample_rate, + (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), + name, sep, + name, sep); + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + f->timestamp, f->duration); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER); + + n = ngx_write_fd(fd, buffer, p - buffer); + } + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); From 17256cea9eb078049d9d84cf6091656f2e059200 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 21 Dec 2016 17:33:44 +0000 Subject: [PATCH 011/113] Add an description/example on how it work --- README.md | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/README.md b/README.md index d53d0e6b9..ba554d7ba 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,51 @@ ## nginx-rtmp-module + forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. + re-add the possibility to have variant in dash like in this configuration + + rtmp { + server { + listen 1935; + + application ingest { + live on; + exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name + -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 1024x576 -b:v 1024K -bufsize 1024k + -f flv rtmp://localhost/dash/$name_hi + -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 640x360 -b:v 832K -bufsize 832k + -f flv rtmp://localhost/dash/$name_med + -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 320x180 -b:v 256K -bufsize 256k + -f flv rtmp://localhost/dash/$name_low + } + + application dash { + live on; + dash on; + dash_nested off; # should be broken at this stage + dash_path /dev/shm/dash; + dash_fragment 3; + dash_playlist_length 120; + dash_cleanup on; + dash_variant _low bandwidth="256000" width="320" height="180"; + dash_variant _med bandwidth="832000" width="640" height="360"; + dash_variant _hi bandwidth="1024000" width="1024" height="576"; + } + } + } + http { + server { + listen 80; + + location /dash { + alias /dev/shm/dash; + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*'; + } + } + } + + ### Project blog http://nginx-rtmp.blogspot.com From 64de3c66efaebd506808f2060019924db6c0df8e Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 22 Dec 2016 19:24:27 +0000 Subject: [PATCH 012/113] Fix dash nested in a way --- dash/ngx_rtmp_dash_module.c | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index b8796a162..f68174be7 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -292,7 +292,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ssize_t n; ngx_fd_t fd; struct tm tm; - ngx_str_t noname, *name; + //ngx_str_t noname, *name; ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; @@ -416,10 +416,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) n = ngx_write_fd(fd, buffer, p - buffer); - ngx_str_null(&noname); - - name = (dacf->nested ? &noname : &ctx->name); - sep = (dacf->nested ? "" : "-"); + sep = (dacf->nested ? "/" : "-"); var = dacf->variant->elts; if (ctx->has_video) { @@ -499,8 +496,8 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", codec_ctx->sample_rate, (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), - name, sep, - name, sep); + &ctx->name, sep, + &ctx->name, sep); for (i = 0; i < ctx->nfrags; i++) { f = ngx_rtmp_dash_get_frag(s, i); From 1049d8a7e487b029b25ac96c1b77b7f1fc44bdb1 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 22 Dec 2016 19:25:56 +0000 Subject: [PATCH 013/113] Change the comment on the README about nested --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ba554d7ba..ffc06aa2c 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,7 @@ application dash { live on; dash on; - dash_nested off; # should be broken at this stage + dash_nested on; # this work but not separate the variant mpd ; but that ok for now dash_path /dev/shm/dash; dash_fragment 3; dash_playlist_length 120; From fcfba1bab748fdf37ff60e44aa0b1bf9fe80dae4 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 23 Dec 2016 20:12:14 +0000 Subject: [PATCH 014/113] Remove unused variable --- dash/ngx_rtmp_dash_module.c | 1 - 1 file changed, 1 deletion(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index f68174be7..9b2aa8042 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -292,7 +292,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ssize_t n; ngx_fd_t fd; struct tm tm; - //ngx_str_t noname, *name; ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; From f95bfa7efe23368ccc3aea0f61cb883256fd589a Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 2 Apr 2017 10:09:49 +0000 Subject: [PATCH 015/113] Decode on cue point scte35 --- ngx_rtmp_live_module.c | 65 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) diff --git a/ngx_rtmp_live_module.c b/ngx_rtmp_live_module.c index d0ee9c883..856555cb8 100644 --- a/ngx_rtmp_live_module.c +++ b/ngx_rtmp_live_module.c @@ -1157,6 +1157,71 @@ static ngx_int_t ngx_rtmp_live_on_cue_point(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { + ngx_int_t res; + + static struct { + double time; + double duration; + u_char name[128]; + u_char type[128]; + u_char ptype[128]; + } v; + + static ngx_rtmp_amf_elt_t in_pr_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.ptype, sizeof(v.ptype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("duration"), + &v.duration, sizeof(v.duration) }, + + }; + + static ngx_rtmp_amf_elt_t in_dt_elts[] = { + + { NGX_RTMP_AMF_NUMBER, + ngx_string("time"), + &v.time, sizeof(v.time) }, + + { NGX_RTMP_AMF_STRING, + ngx_string("name"), + v.name, sizeof(v.name) }, + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.type, sizeof(v.type) }, + + { NGX_RTMP_AMF_OBJECT, + ngx_string("parameters"), + in_pr_elts, sizeof(in_pr_elts) }, + + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + in_dt_elts, sizeof(in_dt_elts) }, + + }; + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "live on cuepoint"); + + ngx_memzero(&v, sizeof(v)); + res = ngx_rtmp_receive_amf(s, in, in_elts, + sizeof(in_elts) / sizeof(in_elts[0])); + + if (res == NGX_OK) { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "onCuepoint : time='%f', name='%s' type='%s' ptype='%s' duration='%f'", + v.time, v.name, v.type, v.ptype, v.duration); + } else { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "onCuepoint : amf not understood"); + } + static ngx_rtmp_amf_elt_t out_elts[] = { { NGX_RTMP_AMF_STRING, From f784f79fe7e747c4528d0802b009fe0850d6e950 Mon Sep 17 00:00:00 2001 From: Cloud User Date: Sun, 9 Apr 2017 19:18:04 +0000 Subject: [PATCH 016/113] Add preliminary support for onCuepoint support on dash --- dash/ngx_rtmp_dash_module.c | 78 +++++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 9b2aa8042..4c536a894 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1963,6 +1963,79 @@ ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v) return next_playlist(s, v); } +static ngx_int_t +ngx_rtmp_dash_on_cue_point(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_int_t res; + + static struct { + double time; + double duration; + u_char name[128]; + u_char type[128]; + u_char ptype[128]; + } v; + + static ngx_rtmp_amf_elt_t in_pr_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.ptype, sizeof(v.ptype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("duration"), + &v.duration, sizeof(v.duration) }, + + }; + + static ngx_rtmp_amf_elt_t in_dt_elts[] = { + + { NGX_RTMP_AMF_NUMBER, + ngx_string("time"), + &v.time, sizeof(v.time) }, + + { NGX_RTMP_AMF_STRING, + ngx_string("name"), + v.name, sizeof(v.name) }, + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.type, sizeof(v.type) }, + + { NGX_RTMP_AMF_OBJECT, + ngx_string("parameters"), + in_pr_elts, sizeof(in_pr_elts) }, + + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + in_dt_elts, sizeof(in_dt_elts) }, + + }; + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash: on cuepoint"); + + ngx_memzero(&v, sizeof(v)); + res = ngx_rtmp_receive_amf(s, in, in_elts, + sizeof(in_elts) / sizeof(in_elts[0])); + + if (res == NGX_OK) { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint : time='%f', name='%s' type='%s' ptype='%s' duration='%f'", + v.time, v.name, v.type, v.ptype, v.duration); + } else { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint : amf not understood"); + } + + return NGX_OK; +} + + static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) { @@ -2046,6 +2119,7 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) { ngx_rtmp_handler_pt *h; ngx_rtmp_core_main_conf_t *cmcf; + ngx_rtmp_amf_handler_t *ch; cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); @@ -2070,5 +2144,9 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) next_playlist = ngx_rtmp_playlist; ngx_rtmp_playlist = ngx_rtmp_dash_playlist; + ch = ngx_array_push(&cmcf->amf); + ngx_str_set(&ch->name, "onCuePoint"); + ch->handler = ngx_rtmp_dash_on_cue_point; + return NGX_OK; } From 9852a07b4d220be8617ca55db3ab2b55522512ea Mon Sep 17 00:00:00 2001 From: Cloud User Date: Mon, 10 Apr 2017 09:51:20 +0000 Subject: [PATCH 017/113] Not needed --- ngx_rtmp_live_module.c | 65 ------------------------------------------ 1 file changed, 65 deletions(-) diff --git a/ngx_rtmp_live_module.c b/ngx_rtmp_live_module.c index 856555cb8..d0ee9c883 100644 --- a/ngx_rtmp_live_module.c +++ b/ngx_rtmp_live_module.c @@ -1157,71 +1157,6 @@ static ngx_int_t ngx_rtmp_live_on_cue_point(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { - ngx_int_t res; - - static struct { - double time; - double duration; - u_char name[128]; - u_char type[128]; - u_char ptype[128]; - } v; - - static ngx_rtmp_amf_elt_t in_pr_elts[] = { - - { NGX_RTMP_AMF_STRING, - ngx_string("type"), - v.ptype, sizeof(v.ptype) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("duration"), - &v.duration, sizeof(v.duration) }, - - }; - - static ngx_rtmp_amf_elt_t in_dt_elts[] = { - - { NGX_RTMP_AMF_NUMBER, - ngx_string("time"), - &v.time, sizeof(v.time) }, - - { NGX_RTMP_AMF_STRING, - ngx_string("name"), - v.name, sizeof(v.name) }, - - { NGX_RTMP_AMF_STRING, - ngx_string("type"), - v.type, sizeof(v.type) }, - - { NGX_RTMP_AMF_OBJECT, - ngx_string("parameters"), - in_pr_elts, sizeof(in_pr_elts) }, - - }; - - static ngx_rtmp_amf_elt_t in_elts[] = { - - { NGX_RTMP_AMF_OBJECT, - ngx_null_string, - in_dt_elts, sizeof(in_dt_elts) }, - - }; - - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "live on cuepoint"); - - ngx_memzero(&v, sizeof(v)); - res = ngx_rtmp_receive_amf(s, in, in_elts, - sizeof(in_elts) / sizeof(in_elts[0])); - - if (res == NGX_OK) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "onCuepoint : time='%f', name='%s' type='%s' ptype='%s' duration='%f'", - v.time, v.name, v.type, v.ptype, v.duration); - } else { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "onCuepoint : amf not understood"); - } - static ngx_rtmp_amf_elt_t out_elts[] = { { NGX_RTMP_AMF_STRING, From cf1d365d25d2c4eb34b8b9651cd0f567704c236e Mon Sep 17 00:00:00 2001 From: Cloud User Date: Fri, 14 Apr 2017 13:02:58 +0000 Subject: [PATCH 018/113] Add support for onCuepoint to emsg box --- dash/ngx_rtmp_dash_module.c | 44 ++++++++++++++++++++++++++++----- dash/ngx_rtmp_dash_templates.h | 5 ++++ dash/ngx_rtmp_mp4.c | 45 ++++++++++++++++++++++++++++++++++ dash/ngx_rtmp_mp4.h | 3 ++- 4 files changed, 90 insertions(+), 7 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 4c536a894..1d9e1f978 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -76,6 +76,10 @@ typedef struct { unsigned opened:1; unsigned has_video:1; unsigned has_audio:1; + unsigned has_cuepoint:1; + + uint32_t cuepoint_timestamp; + uint32_t cuepoint_duration; ngx_file_t video_file; ngx_file_t audio_file; @@ -85,6 +89,7 @@ typedef struct { ngx_rtmp_dash_track_t audio; ngx_rtmp_dash_track_t video; ngx_rtmp_dash_variant_t *var; + } ngx_rtmp_dash_ctx_t; @@ -742,6 +747,14 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) frame_rate, par_x, par_y); + /* + if (ctx->has_cuepoint) { + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_INBAND_EVENT, + ctx->cuepoint_timestamp, + ctx->cuepoint_duration); + } + */ + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, codec_ctx->avc_profile, @@ -956,6 +969,16 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) b.end = buffer + sizeof(buffer); b.pos = b.last = b.start; + if (ctx->has_cuepoint) { + ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, + ctx->cuepoint_timestamp, + ctx->cuepoint_duration); + + pos = b.last; + b.last = pos; + ctx->has_cuepoint = 0; + } + ngx_rtmp_mp4_write_styp(&b); pos = b.last; @@ -1438,7 +1461,6 @@ ngx_rtmp_dash_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) return next_close_stream(s, v); } - static void ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, uint32_t timestamp) @@ -1964,10 +1986,14 @@ ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v) } static ngx_int_t -ngx_rtmp_dash_on_cue_point(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, +ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { - ngx_int_t res; + ngx_int_t res; + ngx_rtmp_dash_ctx_t *ctx; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + static struct { double time; @@ -2025,13 +2051,19 @@ ngx_rtmp_dash_on_cue_point(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, if (res == NGX_OK) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint : time='%f', name='%s' type='%s' ptype='%s' duration='%f'", - v.time, v.name, v.type, v.ptype, v.duration); + "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'", + h->timestamp, v.time, v.name, v.type, v.ptype, v.duration); } else { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint : amf not understood"); } + ctx->has_cuepoint = 1; + ctx->cuepoint_timestamp = h->timestamp; + ctx->cuepoint_duration = v.duration; + + //ngx_rtmp_dash_update_fragments_oncuepoint(s, h->timestamp); + return NGX_OK; } @@ -2146,7 +2178,7 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) ch = ngx_array_push(&cmcf->amf); ngx_str_set(&ch->name, "onCuePoint"); - ch->handler = ngx_rtmp_dash_on_cue_point; + ch->handler = ngx_rtmp_dash_on_cuepoint; return NGX_OK; } diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 1ac99cdab..47cb7d343 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -22,6 +22,11 @@ #define NGX_RTMP_DASH_MANIFEST_PERIOD \ " \n" +#define NGX_RTMP_DASH_INBAND_EVENT \ + " \n" #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ " ", sizeof("")); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; + +} + diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 697b6c87c..2a7028faf 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -47,6 +47,7 @@ ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, uint32_t earliest_pres_time, uint32_t latest_pres_time); ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); - +ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, + uint32_t pres_time, uint32_t cuepoint_time, uint32_t duration_time); #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ From 157f6e6dc98971fc64ed9950c16e056d441d11bc Mon Sep 17 00:00:00 2001 From: Cloud User Date: Tue, 16 May 2017 15:04:05 +0000 Subject: [PATCH 019/113] Add scte_event support ; seems working well --- dash/ngx_rtmp_dash_module.c | 141 ++++++++++++++++++++++++++++++--- dash/ngx_rtmp_dash_templates.h | 3 +- dash/ngx_rtmp_mp4.c | 23 +++--- dash/ngx_rtmp_mp4.h | 2 +- ngx_rtmp_live_module.c | 2 +- 5 files changed, 145 insertions(+), 26 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 1d9e1f978..2a5aae8ca 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -80,6 +80,7 @@ typedef struct { uint32_t cuepoint_timestamp; uint32_t cuepoint_duration; + uint32_t cuepoint_prgid; ngx_file_t video_file; ngx_file_t audio_file; @@ -458,6 +459,8 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) frame_rate, par_x, par_y); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); + for (j = 0; j < dacf->variant->nelts; j++, var++) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, @@ -747,13 +750,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) frame_rate, par_x, par_y); - /* - if (ctx->has_cuepoint) { - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_INBAND_EVENT, - ctx->cuepoint_timestamp, - ctx->cuepoint_duration); - } - */ + p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, @@ -972,7 +969,8 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) if (ctx->has_cuepoint) { ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, ctx->cuepoint_timestamp, - ctx->cuepoint_duration); + ctx->cuepoint_duration, + ctx->cuepoint_prgid); pos = b.last; b.last = pos; @@ -1994,7 +1992,6 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - static struct { double time; double duration; @@ -2043,8 +2040,6 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, }; - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash: on cuepoint"); - ngx_memzero(&v, sizeof(v)); res = ngx_rtmp_receive_amf(s, in, in_elts, sizeof(in_elts) / sizeof(in_elts[0])); @@ -2062,7 +2057,123 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ctx->cuepoint_timestamp = h->timestamp; ctx->cuepoint_duration = v.duration; - //ngx_rtmp_dash_update_fragments_oncuepoint(s, h->timestamp); + return NGX_OK; +} + +static ngx_int_t +ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_int_t res; + ngx_rtmp_dash_ctx_t *ctx; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + static struct { + unsigned ooni; + unsigned splice_event_ci; + unsigned splice_imd; + double avail_num; + double avail_expected; + double duration; + double prtime; + double prgid; + double sctype; + double sevtid; + u_char type[128]; + u_char mtype[128]; + } v; + + static ngx_rtmp_amf_elt_t in_pr_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("messageType"), + v.mtype, sizeof(v.mtype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("splice_command_type"), + &v.sctype, sizeof(v.sctype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("splice_event_id"), + &v.sevtid, sizeof(v.sevtid) }, + + { NGX_RTMP_AMF_BOOLEAN, + ngx_string("splice_event_cancel_indicator"), + &v.splice_event_ci, sizeof(v.splice_event_ci) }, + + { NGX_RTMP_AMF_BOOLEAN, + ngx_string("out_of_network_indicator"), + &v.ooni, sizeof(v.ooni) }, + + { NGX_RTMP_AMF_BOOLEAN, + ngx_string("splice_immediate"), + &v.splice_imd, sizeof(v.splice_imd) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("pre_roll_time"), + &v.prtime, sizeof(v.prtime) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("break_duration"), + &v.duration, sizeof(v.duration) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("unique_program_id"), + &v.prgid, sizeof(v.prgid) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("avail_num"), + &v.avail_num, sizeof(v.avail_num) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("avail_expected"), + &v.avail_expected, sizeof(v.avail_expected) }, + + }; + + static ngx_rtmp_amf_elt_t in_dt_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.type, sizeof(v.type) }, + + { NGX_RTMP_AMF_OBJECT, + ngx_string("parameters"), + in_pr_elts, sizeof(in_pr_elts) }, + + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + in_dt_elts, sizeof(in_dt_elts) }, + + }; + + ngx_memzero(&v, sizeof(v)); + res = ngx_rtmp_receive_amf(s, in, in_elts, + sizeof(in_elts) / sizeof(in_elts[0])); + + if (res == NGX_OK && v.duration > 0) { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint_scte35 : ts='%ui', type='%s', mtype='%s', sctype='%f', "\ + "scid='%f', prgid='%f', duration='%f', avail_num='%f', avail_expected='%f'", + h->timestamp, v.type, v.mtype, v.sctype, + v.sevtid, v.prgid, v.duration, v.avail_num, v.avail_expected); + } else { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint_scte35 : amf not understood"); + } + + if (v.duration > 0) { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint_scte35 : fired" ); + ctx->has_cuepoint = 1; + ctx->cuepoint_timestamp = h->timestamp; + ctx->cuepoint_duration = v.duration; + ctx->cuepoint_prgid = v.prgid; + } return NGX_OK; } @@ -2177,8 +2288,12 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) ngx_rtmp_playlist = ngx_rtmp_dash_playlist; ch = ngx_array_push(&cmcf->amf); - ngx_str_set(&ch->name, "onCuePoint"); + ngx_str_set(&ch->name, "onCuePoint_noop"); ch->handler = ngx_rtmp_dash_on_cuepoint; + ch = ngx_array_push(&cmcf->amf); + ngx_str_set(&ch->name, "onCuePoint"); + ch->handler = ngx_rtmp_dash_on_cuepoint_scte35; + return NGX_OK; } diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 47cb7d343..135b2b92f 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -25,8 +25,7 @@ #define NGX_RTMP_DASH_INBAND_EVENT \ " \n" + " value=\"1\" />\n" #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ " \ + \ + \ + \ +" /* data */ - ngx_rtmp_mp4_data(b, "", sizeof("")); + ngx_rtmp_mp4_data(b, SCTE_EVENT, sizeof(SCTE_EVENT)); ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 2a7028faf..3f1615005 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -48,6 +48,6 @@ ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, uint32_t latest_pres_time); ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t pres_time, uint32_t cuepoint_time, uint32_t duration_time); + uint32_t pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t prg_id); #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ diff --git a/ngx_rtmp_live_module.c b/ngx_rtmp_live_module.c index d0ee9c883..a9caaffd5 100644 --- a/ngx_rtmp_live_module.c +++ b/ngx_rtmp_live_module.c @@ -1257,7 +1257,7 @@ ngx_rtmp_live_on_fi(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, if (res == NGX_OK) { - ngx_log_error(NGX_LOG_DEBUG, s->connection->log, 0, + ngx_log_debug2(NGX_LOG_DEBUG, s->connection->log, 0, "live: onFi: date='%s', time='%s'", v.date, v.time); From 472b677c805e809fd57ebe69712b6ab66c9140fd Mon Sep 17 00:00:00 2001 From: Cloud User Date: Tue, 16 May 2017 15:08:08 +0000 Subject: [PATCH 020/113] Update the readme --- README.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index ffc06aa2c..40b068a0c 100644 --- a/README.md +++ b/README.md @@ -2,8 +2,11 @@ ## nginx-rtmp-module - forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. - re-add the possibility to have variant in dash like in this configuration + Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. + + - add the possibility to have dash variant like in HLS (show below configuration, using ffmpeg to trancode in 3 variants) + - add the support of inband scte event, from rtmp AMF event to dash (InbandEvent in manifest and emsg box in mp4 fragment) + rtmp { server { From 8a4b642e7ae8c3baf62e0133320735e8b42a0fec Mon Sep 17 00:00:00 2001 From: Cloud User Date: Tue, 16 May 2017 15:11:29 +0000 Subject: [PATCH 021/113] Indentation in markdown :/ --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 40b068a0c..e1b648cb0 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,16 @@ ## nginx-rtmp-module - Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. - +Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. + - add the possibility to have dash variant like in HLS (show below configuration, using ffmpeg to trancode in 3 variants) - add the support of inband scte event, from rtmp AMF event to dash (InbandEvent in manifest and emsg box in mp4 fragment) - rtmp { - server { + rtmp { + server { listen 1935; - + application ingest { live on; exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name @@ -22,7 +22,7 @@ -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 320x180 -b:v 256K -bufsize 256k -f flv rtmp://localhost/dash/$name_low } - + application dash { live on; dash on; @@ -40,7 +40,7 @@ http { server { listen 80; - + location /dash { alias /dev/shm/dash; add_header Cache-Control no-cache; From dad60351ba498571a7ba41da6005ff2164a0e888 Mon Sep 17 00:00:00 2001 From: Cloud User Date: Tue, 16 May 2017 15:17:38 +0000 Subject: [PATCH 022/113] Grr --- README.md | 75 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 38 insertions(+), 37 deletions(-) diff --git a/README.md b/README.md index e1b648cb0..b6322c707 100644 --- a/README.md +++ b/README.md @@ -7,47 +7,48 @@ Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to dat - add the possibility to have dash variant like in HLS (show below configuration, using ffmpeg to trancode in 3 variants) - add the support of inband scte event, from rtmp AMF event to dash (InbandEvent in manifest and emsg box in mp4 fragment) - - rtmp { - server { - listen 1935; - - application ingest { - live on; - exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name - -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 1024x576 -b:v 1024K -bufsize 1024k - -f flv rtmp://localhost/dash/$name_hi - -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 640x360 -b:v 832K -bufsize 832k - -f flv rtmp://localhost/dash/$name_med - -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 320x180 -b:v 256K -bufsize 256k - -f flv rtmp://localhost/dash/$name_low - } +``` + rtmp { + server { + listen 1935; + + application ingest { + live on; + exec /usr/bin/ffmpeg -i rtmp://localhost/$app/$name \ + -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 1024x576 -b:v 1024K -bufsize 1024k \ + -f flv rtmp://localhost/dash/$name_hi \ + -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 640x360 -b:v 832K -bufsize 832k \ + -f flv rtmp://localhost/dash/$name_med \ + -c:a libfdk_aac -b:a 64k -c:v libx264 -preset fast -profile:v baseline -vsync cfr -s 320x180 -b:v 256K -bufsize 256k \ + -f flv rtmp://localhost/dash/$name_low + } - application dash { - live on; - dash on; - dash_nested on; # this work but not separate the variant mpd ; but that ok for now - dash_path /dev/shm/dash; - dash_fragment 3; - dash_playlist_length 120; - dash_cleanup on; - dash_variant _low bandwidth="256000" width="320" height="180"; - dash_variant _med bandwidth="832000" width="640" height="360"; - dash_variant _hi bandwidth="1024000" width="1024" height="576"; - } - } + application dash { + live on; + dash on; + dash_nested on; # this work but not separate the variant mpd + dash_path /dev/shm/dash; + dash_fragment 2; # 2 second is generaly a good choice for live + dash_playlist_length 120; # keep 240s of tail + dash_cleanup on; + dash_variant _low bandwidth="256000" width="320" height="180"; + dash_variant _med bandwidth="832000" width="640" height="360"; + dash_variant _hi bandwidth="1024000" width="1024" height="576"; } - http { - server { - listen 80; + } + + http { + server { + listen 80; - location /dash { - alias /dev/shm/dash; - add_header Cache-Control no-cache; - add_header 'Access-Control-Allow-Origin' '*'; - } - } + location /dash { + alias /dev/shm/dash; + add_header Cache-Control no-cache; + add_header 'Access-Control-Allow-Origin' '*'; + } } + } +``` ### Project blog From 0c53af18e3cf7adfc6a98a77d2503cefb17a9dcf Mon Sep 17 00:00:00 2001 From: Cloud User Date: Wed, 17 May 2017 14:36:42 +0000 Subject: [PATCH 023/113] Better level loging --- dash/ngx_rtmp_dash_module.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 2a5aae8ca..4aae9d594 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -967,6 +967,11 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) b.pos = b.last = b.start; if (ctx->has_cuepoint) { + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint write emsg : epts='%ui', cpts='%ui', duration='%ui'", + t->earliest_pres_time, ctx->cuepoint_timestamp, ctx->cuepoint_duration); + ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, ctx->cuepoint_timestamp, ctx->cuepoint_duration, @@ -2049,7 +2054,7 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'", h->timestamp, v.time, v.name, v.type, v.ptype, v.duration); } else { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash : onCuepoint : amf not understood"); } @@ -2163,12 +2168,11 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, h->timestamp, v.type, v.mtype, v.sctype, v.sevtid, v.prgid, v.duration, v.avail_num, v.avail_expected); } else { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash : onCuepoint_scte35 : amf not understood"); } if (v.duration > 0) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint_scte35 : fired" ); ctx->has_cuepoint = 1; ctx->cuepoint_timestamp = h->timestamp; ctx->cuepoint_duration = v.duration; From f7e94c19289c1171c9779bcb172c5287f7fe87d1 Mon Sep 17 00:00:00 2001 From: Cloud User Date: Fri, 19 May 2017 14:06:56 +0000 Subject: [PATCH 024/113] Some fix and hack for dahsjs --- dash/ngx_rtmp_dash_module.c | 6 +++--- dash/ngx_rtmp_mp4.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 4aae9d594..b2ac9925e 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -969,10 +969,10 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) if (ctx->has_cuepoint) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint write emsg : epts='%ui', cpts='%ui', duration='%ui'", - t->earliest_pres_time, ctx->cuepoint_timestamp, ctx->cuepoint_duration); + "dash : onCuepoint write emsg : epts='%ui', lpts='%ui', cpts='%ui', duration='%ui'", + t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_timestamp, ctx->cuepoint_duration); - ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, + ngx_rtmp_mp4_write_emsg(&b, 0, /* should be presentation time but dashjs is buggy */ ctx->cuepoint_timestamp, ctx->cuepoint_duration, ctx->cuepoint_prgid); diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index bcae03ed2..4e42b64fc 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1168,7 +1168,7 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t prg_id) + uint32_t pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id) { u_char *pos; uint32_t delta_time; @@ -1197,14 +1197,14 @@ ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, ngx_rtmp_mp4_field_32(b, duration_time); /* id */ - ngx_rtmp_mp4_field_32(b, prg_id); + ngx_rtmp_mp4_field_32(b, id); #define SCTE_EVENT "\ \ \ \ -" +" /* data */ ngx_rtmp_mp4_data(b, SCTE_EVENT, sizeof(SCTE_EVENT)); From 56e907b2eaaf2aaed2310ad7203d4ec984b7e786 Mon Sep 17 00:00:00 2001 From: Cloud User Date: Fri, 26 May 2017 09:46:28 +0000 Subject: [PATCH 025/113] Some cleaning --- dash/ngx_rtmp_dash_module.c | 19 ++++++++++--------- dash/ngx_rtmp_mp4.c | 4 ++-- dash/ngx_rtmp_mp4.h | 2 +- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index b2ac9925e..8d6ef47e2 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -78,9 +78,9 @@ typedef struct { unsigned has_audio:1; unsigned has_cuepoint:1; - uint32_t cuepoint_timestamp; + uint32_t cuepoint_time; uint32_t cuepoint_duration; - uint32_t cuepoint_prgid; + uint32_t cuepoint_id; ngx_file_t video_file; ngx_file_t audio_file; @@ -970,12 +970,13 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint write emsg : epts='%ui', lpts='%ui', cpts='%ui', duration='%ui'", - t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_timestamp, ctx->cuepoint_duration); + t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_time, ctx->cuepoint_duration); - ngx_rtmp_mp4_write_emsg(&b, 0, /* should be presentation time but dashjs is buggy */ - ctx->cuepoint_timestamp, + /* ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, */ + ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ + ctx->cuepoint_time, ctx->cuepoint_duration, - ctx->cuepoint_prgid); + ctx->cuepoint_id); pos = b.last; b.last = pos; @@ -2059,7 +2060,7 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } ctx->has_cuepoint = 1; - ctx->cuepoint_timestamp = h->timestamp; + ctx->cuepoint_time = h->timestamp; ctx->cuepoint_duration = v.duration; return NGX_OK; @@ -2174,9 +2175,9 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, if (v.duration > 0) { ctx->has_cuepoint = 1; - ctx->cuepoint_timestamp = h->timestamp; + ctx->cuepoint_time = h->timestamp; ctx->cuepoint_duration = v.duration; - ctx->cuepoint_prgid = v.prgid; + ctx->cuepoint_id = v.prgid; } return NGX_OK; diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 4e42b64fc..7ab73e73c 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1168,13 +1168,13 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id) + uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id) { u_char *pos; uint32_t delta_time; uint32_t timescale = 1000; - delta_time = (cuepoint_time - pres_time); + delta_time = cuepoint_time - earliest_pres_time; pos = ngx_rtmp_mp4_start_box(b, "emsg"); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 3f1615005..5dc69b270 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -48,6 +48,6 @@ ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, uint32_t latest_pres_time); ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t prg_id); + uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id); #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ From 095ccc9db756b87141477c1b4b4c422d1e39daa1 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 1 Jun 2017 16:02:46 +0000 Subject: [PATCH 026/113] Add max flag to ensure the manifest was only write once --- README.md | 337 +----------------------------------- dash/ngx_rtmp_dash_module.c | 7 +- 2 files changed, 7 insertions(+), 337 deletions(-) diff --git a/README.md b/README.md index b6322c707..12e0ba8d7 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,8 @@ Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. - - add the possibility to have dash variant like in HLS (show below configuration, using ffmpeg to trancode in 3 variants) + - add the possibility to have dash variant like in HLS (show below configuration, using ffmpeg to trancode in 3 variants). + note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest. - add the support of inband scte event, from rtmp AMF event to dash (InbandEvent in manifest and emsg box in mp4 fragment) ``` @@ -33,7 +34,7 @@ Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to dat dash_cleanup on; dash_variant _low bandwidth="256000" width="320" height="180"; dash_variant _med bandwidth="832000" width="640" height="360"; - dash_variant _hi bandwidth="1024000" width="1024" height="576"; + dash_variant _hi bandwidth="1024000" width="1024" height="576" max; } } @@ -51,335 +52,3 @@ Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to dat ``` -### Project blog - - http://nginx-rtmp.blogspot.com - -### Documentation - -* [Home](doc/README.md) -* [Control module](doc/control_modul.md) -* [Debug log](doc/debug_log.md) -* [Directives](doc/directives.md) -* [Examples](doc/examples.md) -* [Exec wrapper in bash](doc/exec_wrapper_in_bash.md) -* [FAQ](doc/faq.md) -* [Getting number of subscribers](doc/getting_number_of_subscribers.md) -* [Getting started with nginx rtmp](doc/getting_started.md) -* [Installing in Gentoo](doc/installing_in_gentoo.md) -* [Installing on Ubuntu using PPAs](doc/installing_ubuntu_using_ppas.md) -* [Tutorial](doc/tutorial.md) - -*Source: https://github.com/arut/nginx-rtmp-module/wiki* - -* [Latest updates](doc/README.md#updates) - -### Google group - - https://groups.google.com/group/nginx-rtmp - - https://groups.google.com/group/nginx-rtmp-ru (Russian) - -### Donation page (Paypal etc) - - http://arut.github.com/nginx-rtmp-module/ - -### Features - -* RTMP/HLS/MPEG-DASH live streaming - -* RTMP Video on demand FLV/MP4, - playing from local filesystem or HTTP - -* Stream relay support for distributed - streaming: push & pull models - -* Recording streams in multiple FLVs - -* H264/AAC support - -* Online transcoding with FFmpeg - -* HTTP callbacks (publish/play/record/update etc) - -* Running external programs on certain events (exec) - -* HTTP control module for recording audio/video and dropping clients - -* Advanced buffering techniques - to keep memory allocations at a minimum - level for faster streaming and low - memory footprint - -* Proved to work with Wirecast, FMS, Wowza, - JWPlayer, FlowPlayer, StrobeMediaPlayback, - ffmpeg, avconv, rtmpdump, flvstreamer - and many more - -* Statistics in XML/XSL in machine- & human- - readable form - -* Linux/FreeBSD/MacOS/Windows - -### Build - -cd to NGINX source directory & run this: - - ./configure --add-module=/path/to/nginx-rtmp-module - make - make install - -Several versions of nginx (1.3.14 - 1.5.0) require http_ssl_module to be -added as well: - - ./configure --add-module=/path/to/nginx-rtmp-module --with-http_ssl_module - -For building debug version of nginx add `--with-debug` - - ./configure --add-module=/path/to-nginx/rtmp-module --with-debug - -[Read more about debug log](https://github.com/arut/nginx-rtmp-module/wiki/Debug-log) - -### Contributing and Branch Policy - -The "dev" branch is the one where all contributions will be merged before reaching "master". -If you plan to propose a patch, please commit into the "dev" branch or its own feature branch. -Direct commit to "master" are not permitted. - -### Windows limitations - -Windows support is limited. These features are not supported - -* execs -* static pulls -* auto_push - -### RTMP URL format - - rtmp://rtmp.example.com/app[/name] - -app - should match one of application {} - blocks in config - -name - interpreted by each application - can be empty - - -### Multi-worker live streaming - -This NGINX-RTMP module does not support multi-worker live -streaming. While this feature can be enabled through rtmp_auto_push on|off directive, it is ill advised because it is incompatible with NGINX versions starting 1.7.2 and up, there for it should not be used. - - -### Example nginx.conf - - rtmp { - - server { - - listen 1935; - - chunk_size 4000; - - # TV mode: one publisher, many subscribers - application mytv { - - # enable live streaming - live on; - - # record first 1K of stream - record all; - record_path /tmp/av; - record_max_size 1K; - - # append current timestamp to each flv - record_unique on; - - # publish only from localhost - allow publish 127.0.0.1; - deny publish all; - - #allow play all; - } - - # Transcoding (ffmpeg needed) - application big { - live on; - - # On every pusblished stream run this command (ffmpeg) - # with substitutions: $app/${app}, $name/${name} for application & stream name. - # - # This ffmpeg call receives stream from this application & - # reduces the resolution down to 32x32. The stream is the published to - # 'small' application (see below) under the same name. - # - # ffmpeg can do anything with the stream like video/audio - # transcoding, resizing, altering container/codec params etc - # - # Multiple exec lines can be specified. - - exec ffmpeg -re -i rtmp://localhost:1935/$app/$name -vcodec flv -acodec copy -s 32x32 - -f flv rtmp://localhost:1935/small/${name}; - } - - application small { - live on; - # Video with reduced resolution comes here from ffmpeg - } - - application webcam { - live on; - - # Stream from local webcam - exec_static ffmpeg -f video4linux2 -i /dev/video0 -c:v libx264 -an - -f flv rtmp://localhost:1935/webcam/mystream; - } - - application mypush { - live on; - - # Every stream published here - # is automatically pushed to - # these two machines - push rtmp1.example.com; - push rtmp2.example.com:1934; - } - - application mypull { - live on; - - # Pull all streams from remote machine - # and play locally - pull rtmp://rtmp3.example.com pageUrl=www.example.com/index.html; - } - - application mystaticpull { - live on; - - # Static pull is started at nginx start - pull rtmp://rtmp4.example.com pageUrl=www.example.com/index.html name=mystream static; - } - - # video on demand - application vod { - play /var/flvs; - } - - application vod2 { - play /var/mp4s; - } - - # Many publishers, many subscribers - # no checks, no recording - application videochat { - - live on; - - # The following notifications receive all - # the session variables as well as - # particular call arguments in HTTP POST - # request - - # Make HTTP request & use HTTP retcode - # to decide whether to allow publishing - # from this connection or not - on_publish http://localhost:8080/publish; - - # Same with playing - on_play http://localhost:8080/play; - - # Publish/play end (repeats on disconnect) - on_done http://localhost:8080/done; - - # All above mentioned notifications receive - # standard connect() arguments as well as - # play/publish ones. If any arguments are sent - # with GET-style syntax to play & publish - # these are also included. - # Example URL: - # rtmp://localhost/myapp/mystream?a=b&c=d - - # record 10 video keyframes (no audio) every 2 minutes - record keyframes; - record_path /tmp/vc; - record_max_frames 10; - record_interval 2m; - - # Async notify about an flv recorded - on_record_done http://localhost:8080/record_done; - - } - - - # HLS - - # For HLS to work please create a directory in tmpfs (/tmp/hls here) - # for the fragments. The directory contents is served via HTTP (see - # http{} section in config) - # - # Incoming stream must be in H264/AAC. For iPhones use baseline H264 - # profile (see ffmpeg example). - # This example creates RTMP stream from movie ready for HLS: - # - # ffmpeg -loglevel verbose -re -i movie.avi -vcodec libx264 - # -vprofile baseline -acodec libmp3lame -ar 44100 -ac 1 - # -f flv rtmp://localhost:1935/hls/movie - # - # If you need to transcode live stream use 'exec' feature. - # - application hls { - live on; - hls on; - hls_path /tmp/hls; - } - - # MPEG-DASH is similar to HLS - - application dash { - live on; - dash on; - dash_path /tmp/dash; - } - } - } - - # HTTP can be used for accessing RTMP stats - http { - - server { - - listen 8080; - - # This URL provides RTMP statistics in XML - location /stat { - rtmp_stat all; - - # Use this stylesheet to view XML as web page - # in browser - rtmp_stat_stylesheet stat.xsl; - } - - location /stat.xsl { - # XML stylesheet to view RTMP stats. - # Copy stat.xsl wherever you want - # and put the full directory path here - root /path/to/stat.xsl/; - } - - location /hls { - # Serve HLS fragments - types { - application/vnd.apple.mpegurl m3u8; - video/mp2t ts; - } - root /tmp; - add_header Cache-Control no-cache; - } - - location /dash { - # Serve DASH fragments - root /tmp; - add_header Cache-Control no-cache; - } - } - } diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 8d6ef47e2..b8df2e671 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -461,8 +461,8 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); - for (j = 0; j < dacf->variant->nelts; j++, var++) - { + for (j = 0; j < dacf->variant->nelts; j++, var++) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, &ctx->varname, &var->suffix, codec_ctx->avc_profile, @@ -851,7 +851,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } - if (ctx->var) { + /* try to write the variant file only once, check the max flag */ + if (ctx->var->args.nelts > 3) { return ngx_rtmp_dash_write_variant_playlist(s); } From 72cfb173c7b8150d8905c4ba6b6fa4a8e43f39cf Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 1 Jun 2017 16:21:59 +0000 Subject: [PATCH 027/113] Authors ? --- AUTHORS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/AUTHORS b/AUTHORS index 151f456f1..27a3e775c 100644 --- a/AUTHORS +++ b/AUTHORS @@ -13,3 +13,10 @@ Fork author: Contacts: sergey.dryabzhinsky@gmail.com + +Fork ot the fork author: + Raphael Mazelier + Paris, France + + Contacts: + raph@futomaki.net From 9951c17f65d1438955409d70c3cfbceac54854ba Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 1 Jun 2017 16:29:09 +0000 Subject: [PATCH 028/113] Add a todolist --- TODO | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 TODO diff --git a/TODO b/TODO new file mode 100644 index 000000000..a8d504647 --- /dev/null +++ b/TODO @@ -0,0 +1,3 @@ +- rewritte the variant code for dash, use separate file, and concat them in the manifest +- add an option for inbant event handling, allow the choice of using onCuePoint/onCuePoint_scte35 +- add support for common encryption, early stage in aes_ctr.c From 025ac8ac77bc960e068d8e31598ddbbab1bbd0a5 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 1 Jun 2017 16:53:53 +0000 Subject: [PATCH 029/113] Better not write max flag --- dash/ngx_rtmp_dash_module.c | 2 +- dash/ngx_rtmp_mp4.c | 2 +- dash/ngx_rtmp_mp4.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index b8df2e671..5b2052dbb 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -470,7 +470,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) codec_ctx->avc_level); arg = var->args.elts; - for (k = 0; k < var->args.nelts; k++, arg++) { + for (k = 0; k < var->args.nelts && k < 2 ; k++, arg++) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); } diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 7ab73e73c..34745e181 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1156,7 +1156,7 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, } -ngx_uint_t +ngx_int_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) { ngx_rtmp_mp4_field_32(b, size); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 5dc69b270..ab010213b 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -46,7 +46,7 @@ ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, uint32_t earliest_pres_time, uint32_t latest_pres_time); -ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); +ngx_int_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id); From bdb64187f680b17caf9aaa0a84965d30f048ef30 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 6 Jun 2017 11:53:59 +0000 Subject: [PATCH 030/113] Fix case when chain amf handler --- dash/ngx_rtmp_dash_module.c | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 5b2052dbb..285f05361 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1998,6 +1998,9 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_dash_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + if (ctx == NULL ) { + return NGX_OK; + } static struct { double time; @@ -2075,6 +2078,9 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_dash_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + if (ctx == NULL ) { + return NGX_OK; + } static struct { unsigned ooni; From 59492673e7d5002c85d5e52219695407d9a0daf6 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 13 Jun 2017 15:51:20 +0000 Subject: [PATCH 031/113] Add switch to not add inbandeventstream when ad_markers is not enabled --- dash/ngx_rtmp_dash_module.c | 79 ++++++++++++++++++++++++++++++------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 285f05361..e22bf40fa 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -113,6 +113,17 @@ static ngx_conf_enum_t ngx_rtmp_dash_clock_compensation_type_sl { ngx_null_string, 0 } }; +#define NGX_RTMP_DASH_AD_MARKERS_OFF 1 +#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT 2 +#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 3 + +static ngx_conf_enum_t ngx_rtmp_dash_ad_markers_type_slots[] = { + { ngx_string("off"), NGX_RTMP_DASH_AD_MARKERS_OFF }, + { ngx_string("on_cuepoint"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT }, + { ngx_string("on_cuepoint_scte35"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 }, + { ngx_null_string, 0 } +}; + typedef struct { ngx_flag_t dash; ngx_msec_t fraglen; @@ -128,6 +139,7 @@ typedef struct { ngx_flag_t cleanup; ngx_path_t *slot; ngx_array_t *variant; + ngx_uint_t ad_markers; } ngx_rtmp_dash_app_conf_t; @@ -196,6 +208,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { 0, NULL }, + { ngx_string("dash_ad_markers"), + 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_dash_app_conf_t, ad_markers), + &ngx_rtmp_dash_ad_markers_type_slots }, + ngx_null_command }; @@ -459,7 +478,11 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) frame_rate, par_x, par_y); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); + switch (dacf->ad_markers) { + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: + p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); + } for (j = 0; j < dacf->variant->nelts; j++, var++) { @@ -750,7 +773,11 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) frame_rate, par_x, par_y); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); + switch (dacf->ad_markers) { + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: + p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); + } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, @@ -852,7 +879,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } /* try to write the variant file only once, check the max flag */ - if (ctx->var->args.nelts > 3) { + if (ctx->var && ctx->var->args.nelts > 3) { return ngx_rtmp_dash_write_variant_playlist(s); } @@ -1913,6 +1940,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) } } + #if (nginx_version >= 1011005) static ngx_msec_t #else @@ -1933,6 +1961,7 @@ ngx_rtmp_dash_cleanup(void *data) #endif } + static char * ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { @@ -1984,12 +2013,14 @@ ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) return NGX_CONF_OK; } + static ngx_int_t ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v) { return next_playlist(s, v); } + static ngx_int_t ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) @@ -1998,9 +2029,6 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_dash_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - if (ctx == NULL ) { - return NGX_OK; - } static struct { double time; @@ -2070,6 +2098,7 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } + static ngx_int_t ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) @@ -2078,9 +2107,6 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_dash_ctx_t *ctx; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - if (ctx == NULL ) { - return NGX_OK; - } static struct { unsigned ooni; @@ -2191,6 +2217,32 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } +static ngx_int_t +ngx_rtmp_dash_ad_markers(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { + return NGX_OK; + } + + switch (dacf->ad_markers) { + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: + return ngx_rtmp_dash_on_cuepoint(s, h, in); + break; + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: + return ngx_rtmp_dash_on_cuepoint_scte35(s, h, in); + break; + default: + return NGX_OK; + } + + return NGX_OK; +} + + static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) { @@ -2207,6 +2259,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) conf->cleanup = NGX_CONF_UNSET; conf->nested = NGX_CONF_UNSET; conf->clock_compensation = NGX_CONF_UNSET; + conf->ad_markers = NGX_CONF_UNSET; return conf; } @@ -2227,6 +2280,8 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); + ngx_conf_merge_uint_value(conf->ad_markers, prev->ad_markers, + NGX_RTMP_DASH_AD_MARKERS_OFF); if (conf->fraglen) { conf->winfrags = conf->playlen / conf->fraglen; @@ -2299,13 +2354,9 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) next_playlist = ngx_rtmp_playlist; ngx_rtmp_playlist = ngx_rtmp_dash_playlist; - ch = ngx_array_push(&cmcf->amf); - ngx_str_set(&ch->name, "onCuePoint_noop"); - ch->handler = ngx_rtmp_dash_on_cuepoint; - ch = ngx_array_push(&cmcf->amf); ngx_str_set(&ch->name, "onCuePoint"); - ch->handler = ngx_rtmp_dash_on_cuepoint_scte35; + ch->handler = ngx_rtmp_dash_ad_markers; return NGX_OK; } From fc899f38df9ef4cec18ed2566772b936460e822b Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 26 Jun 2017 12:42:25 +0000 Subject: [PATCH 032/113] Some clarification and fix for elemental live --- ngx_rtmp_handler.c | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index a8bef611a..84b75b70e 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -398,10 +398,13 @@ ngx_rtmp_recv(ngx_event_t *rev) if (b->last - p < 4) continue; pp = (u_char*)×tamp; + /* extented time stamp: + * big-endian 4b -> little-endian 4b */ pp[3] = *p++; pp[2] = *p++; pp[1] = *p++; pp[0] = *p++; + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP extended timestamp %uD", timestamp); } if (st->len == 0) { @@ -412,10 +415,17 @@ ngx_rtmp_recv(ngx_event_t *rev) * in real life */ st->ext = (ext && cscf->publish_time_fix); if (fmt) { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); st->dtime = timestamp; } else { - h->timestamp = timestamp; - st->dtime = 0; + /* fix elemental live server sending wrong ts */ + if ((int)h->type == 18) { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18 and zero lenght", (int)fmt); + } else { + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP absolute timestamp on fmt type 0"); + h->timestamp = timestamp; + st->dtime = 0; + } } } @@ -430,7 +440,7 @@ ngx_rtmp_recv(ngx_event_t *rev) if (h->mlen > cscf->max_message) { ngx_log_error(NGX_LOG_INFO, c->log, 0, - "too big message: %uz", cscf->max_message); + "too big message: %uz > %uz ", h->mlen, cscf->max_message); ngx_rtmp_finalize_session(s); return; } @@ -774,8 +784,8 @@ ngx_rtmp_receive_message(ngx_rtmp_session_t *s, ch = ch->next, ++nbufs); ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "RTMP recv %s (%d) csid=%D timestamp=%D " - "mlen=%D msid=%D nbufs=%d", + "RTMP recv %s (%d) csid=%D timestamp=%uD " + "mlen=%uD msid=%D nbufs=%d", ngx_rtmp_message_type(h->type), (int)h->type, h->csid, h->timestamp, h->mlen, h->msid, nbufs); } From d58ec7d6cbb968d75f5c9f43e2b57522d9da5365 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 27 Jun 2017 20:22:33 +0000 Subject: [PATCH 033/113] Fix timestamp mess with elemental server --- ngx_rtmp_handler.c | 27 ++++++++++++++++----------- 1 file changed, 16 insertions(+), 11 deletions(-) diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index 84b75b70e..6c12675ac 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -404,27 +404,32 @@ ngx_rtmp_recv(ngx_event_t *rev) pp[2] = *p++; pp[1] = *p++; pp[0] = *p++; - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP extended timestamp %uD", timestamp); + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP extended timestamp %uD", (uint32_t)timestamp); } - if (st->len == 0) { - /* Messages with type=3 should - * never have ext timestamp field - * according to standard. - * However that's not always the case - * in real life */ - st->ext = (ext && cscf->publish_time_fix); + if (fmt <= 2) { + /* The specification states that ext timestamp + * is present in Type 3 chunks when the most recent Type 0, + * 1, or 2 chunk for the same chunk stream ID have the presence of + * an extended timestamp field. */ + st->ext = ext; if (fmt) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); - st->dtime = timestamp; + /* another elemental fix relative ts to epoch + TODO : make this configurable */ + if (timestamp >= s->peer_epoch) { + st->dtime = timestamp - s->peer_epoch; + } else { + st->dtime = timestamp; + } } else { - /* fix elemental live server sending wrong ts */ + /* fix elemental live server sending garbage ts */ if ((int)h->type == 18) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18 and zero lenght", (int)fmt); } else { - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP absolute timestamp on fmt type 0"); h->timestamp = timestamp; st->dtime = 0; + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP absolute timestamp on fmt type 0 : %uD", h->timestamp); } } } From ed48ca38eedca70e9aade27ba7545dde95a135ab Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 30 Jun 2017 13:13:06 +0000 Subject: [PATCH 034/113] Revert change to something working --- dash/ngx_rtmp_dash_module.c | 60 +++++++++++++++++++++++++++++++++---- ngx_rtmp_handler.c | 4 ++- 2 files changed, 58 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e22bf40fa..e5cfd15b9 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -60,6 +60,8 @@ typedef struct { typedef struct { + ngx_str_t segments; + ngx_str_t segments_bak; ngx_str_t playlist; ngx_str_t playlist_bak; ngx_str_t var_playlist; @@ -605,7 +607,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) char *sep; u_char *p, *last; ssize_t n; - ngx_fd_t fd; + ngx_fd_t fd, fds; struct tm tm; ngx_str_t noname, *name; ngx_uint_t i, frame_rate_num, frame_rate_denom; @@ -647,6 +649,16 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) "dash: open failed: '%V'", &ctx->playlist_bak); return NGX_ERROR; } + + /* write segments file */ + fds = ngx_open_file(ctx->segments_bak.data, NGX_FILE_WRONLY, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (fds == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: open failed: '%V'", &ctx->segments_bak); + return NGX_ERROR; + } /** * Availability time must be equal stream start time @@ -778,8 +790,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } + + n = ngx_write_fd(fd, buffer, p - buffer); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, codec_ctx->avc_profile, codec_ctx->avc_compat, @@ -799,6 +813,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); + ngx_write_fd(fds, buffer, p - buffer); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); @@ -868,6 +884,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } ngx_close_file(fd); + ngx_close_file(fds); if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data) == NGX_FILE_ERROR) @@ -878,6 +895,15 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } + if (ngx_rtmp_dash_rename_file(ctx->segments_bak.data, ctx->segments.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%V'->'%V'", + &ctx->segments_bak, &ctx->segments); + return NGX_ERROR; + } + /* try to write the variant file only once, check the max flag */ if (ctx->var && ctx->var->args.nelts > 3) { return ngx_rtmp_dash_write_variant_playlist(s); @@ -1455,9 +1481,33 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) *p = 0; - ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: playlist='%V' playlist_bak='%V' stream_pattern='%V'", - &ctx->playlist, &ctx->playlist_bak, &ctx->stream); + /* segments path */ + + ctx->segments.data = ngx_palloc(s->connection->pool, + ctx->playlist.len - 4 + sizeof(".seg")); + p = ngx_cpymem(ctx->segments.data, ctx->playlist.data, + ctx->playlist.len - 4); + p = ngx_cpymem(p, ".seg", sizeof(".seg") - 1); + + ctx->segments.len = p - ctx->segments.data; + + *p = 0; + + /* segments bak (new segments) path */ + + ctx->segments_bak.data = ngx_palloc(s->connection->pool, + ctx->playlist.len - 4 + sizeof(".sbk")); + p = ngx_cpymem(ctx->segments_bak.data, ctx->playlist.data, + ctx->playlist.len - 4); + p = ngx_cpymem(p, ".sbk", sizeof(".sbk") - 1); + + ctx->segments_bak.len = p - ctx->segments_bak.data; + + *p = 0; + + ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: playlist='%V' playlist_bak='%V' segments='%V' segments_bak='%V' stream_pattern='%V'", + &ctx->playlist, &ctx->playlist_bak, &ctx->segments, &ctx->segments_bak, &ctx->stream); ctx->start_time = *ngx_cached_time; diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index 6c12675ac..ff150175d 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -416,9 +416,11 @@ ngx_rtmp_recv(ngx_event_t *rev) if (fmt) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); /* another elemental fix relative ts to epoch - TODO : make this configurable */ + * TODO : make this configurable */ if (timestamp >= s->peer_epoch) { st->dtime = timestamp - s->peer_epoch; + } else if ((timestamp + 2000) >= s->peer_epoch) { + st->dtime = 0; } else { st->dtime = timestamp; } From 3ffc69ec04cbebc3fc0b41443e337ed1bebacc1c Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 30 Jun 2017 13:15:08 +0000 Subject: [PATCH 035/113] Initial test of new variant version --- dash/ngx_rtmp_dash_module.c | 2412 ----------------------------------- 1 file changed, 2412 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e5cfd15b9..e69de29bb 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1,2412 +0,0 @@ - - -#include -#include -#include -#include -#include "ngx_rtmp_live_module.h" -#include "ngx_rtmp_mp4.h" -#include "ngx_rtmp_dash_templates.h" - - -static ngx_rtmp_publish_pt next_publish; -static ngx_rtmp_close_stream_pt next_close_stream; -static ngx_rtmp_stream_begin_pt next_stream_begin; -static ngx_rtmp_stream_eof_pt next_stream_eof; -static ngx_rtmp_playlist_pt next_playlist; - - -static char * ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf); -static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf); -static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, - void *parent, void *child); -static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s); -static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s); - - -#define NGX_RTMP_DASH_BUFSIZE (1024*1024) -#define NGX_RTMP_DASH_MAX_MDAT (10*1024*1024) -#define NGX_RTMP_DASH_MAX_SAMPLES 1024 -#define NGX_RTMP_DASH_DIR_ACCESS 0744 - -#define NGX_RTMP_DASH_GMT_LENGTH sizeof("1970-09-28T12:00:00+06:00") - -typedef struct { - uint32_t timestamp; - uint32_t duration; -} ngx_rtmp_dash_frag_t; - - -typedef struct { - ngx_uint_t id; - ngx_uint_t opened; - ngx_uint_t mdat_size; - ngx_uint_t sample_count; - ngx_uint_t sample_mask; - ngx_fd_t fd; - char type; - uint32_t earliest_pres_time; - uint32_t latest_pres_time; - ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; -} ngx_rtmp_dash_track_t; - - -typedef struct { - ngx_str_t suffix; - ngx_array_t args; -} ngx_rtmp_dash_variant_t; - - -typedef struct { - ngx_str_t segments; - ngx_str_t segments_bak; - ngx_str_t playlist; - ngx_str_t playlist_bak; - ngx_str_t var_playlist; - ngx_str_t var_playlist_bak; - ngx_str_t name; - ngx_str_t varname; - ngx_str_t stream; - ngx_time_t start_time; - - ngx_uint_t nfrags; - ngx_uint_t frag; - ngx_rtmp_dash_frag_t *frags; /* circular 2 * winfrags + 1 */ - - unsigned opened:1; - unsigned has_video:1; - unsigned has_audio:1; - unsigned has_cuepoint:1; - - uint32_t cuepoint_time; - uint32_t cuepoint_duration; - uint32_t cuepoint_id; - - ngx_file_t video_file; - ngx_file_t audio_file; - - ngx_uint_t id; - - ngx_rtmp_dash_track_t audio; - ngx_rtmp_dash_track_t video; - ngx_rtmp_dash_variant_t *var; - -} ngx_rtmp_dash_ctx_t; - - -typedef struct { - ngx_str_t path; - ngx_msec_t playlen; -} ngx_rtmp_dash_cleanup_t; - - -#define NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF 1 -#define NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP 2 -#define NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD 3 -#define NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO 4 - -static ngx_conf_enum_t ngx_rtmp_dash_clock_compensation_type_slots[] = { - { ngx_string("off"), NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF }, - { ngx_string("ntp"), NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP }, - { ngx_string("http_head"), NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD }, - { ngx_string("http_iso"), NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO }, - { ngx_null_string, 0 } -}; - -#define NGX_RTMP_DASH_AD_MARKERS_OFF 1 -#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT 2 -#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 3 - -static ngx_conf_enum_t ngx_rtmp_dash_ad_markers_type_slots[] = { - { ngx_string("off"), NGX_RTMP_DASH_AD_MARKERS_OFF }, - { ngx_string("on_cuepoint"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT }, - { ngx_string("on_cuepoint_scte35"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 }, - { ngx_null_string, 0 } -}; - -typedef struct { - ngx_flag_t dash; - ngx_msec_t fraglen; - ngx_msec_t playlen; - ngx_flag_t nested; - ngx_uint_t clock_compensation; // Try to compensate clock drift - // between client and server (on client side) - ngx_str_t clock_helper_uri; // Use uri to static file on HTTP server - // - same machine as RTMP/DASH) - // - or NTP server address - ngx_str_t path; - ngx_uint_t winfrags; - ngx_flag_t cleanup; - ngx_path_t *slot; - ngx_array_t *variant; - ngx_uint_t ad_markers; -} ngx_rtmp_dash_app_conf_t; - - -static ngx_command_t ngx_rtmp_dash_commands[] = { - - { ngx_string("dash"), - 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_dash_app_conf_t, dash), - NULL }, - - { ngx_string("dash_fragment"), - NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_RTMP_APP_CONF_OFFSET, - offsetof(ngx_rtmp_dash_app_conf_t, fraglen), - NULL }, - - { ngx_string("dash_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_dash_app_conf_t, path), - NULL }, - - { ngx_string("dash_playlist_length"), - NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, - ngx_conf_set_msec_slot, - NGX_RTMP_APP_CONF_OFFSET, - offsetof(ngx_rtmp_dash_app_conf_t, playlen), - NULL }, - - { ngx_string("dash_cleanup"), - 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_dash_app_conf_t, cleanup), - NULL }, - - { ngx_string("dash_nested"), - 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_dash_app_conf_t, nested), - NULL }, - - { ngx_string("dash_clock_compensation"), - 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_dash_app_conf_t, clock_compensation), - &ngx_rtmp_dash_clock_compensation_type_slots }, - - { ngx_string("dash_clock_helper_uri"), - 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_dash_app_conf_t, clock_helper_uri), - NULL }, - - { ngx_string("dash_variant"), - NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE, - ngx_rtmp_dash_variant, - NGX_RTMP_APP_CONF_OFFSET, - 0, - NULL }, - - { ngx_string("dash_ad_markers"), - 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_dash_app_conf_t, ad_markers), - &ngx_rtmp_dash_ad_markers_type_slots }, - - ngx_null_command -}; - - -static ngx_rtmp_module_t ngx_rtmp_dash_module_ctx = { - NULL, /* preconfiguration */ - ngx_rtmp_dash_postconfiguration, /* postconfiguration */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - NULL, /* create server configuration */ - NULL, /* merge server configuration */ - - ngx_rtmp_dash_create_app_conf, /* create location configuration */ - ngx_rtmp_dash_merge_app_conf, /* merge location configuration */ -}; - - -ngx_module_t ngx_rtmp_dash_module = { - NGX_MODULE_V1, - &ngx_rtmp_dash_module_ctx, /* module context */ - ngx_rtmp_dash_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 ngx_rtmp_dash_frag_t * -ngx_rtmp_dash_get_frag(ngx_rtmp_session_t *s, ngx_int_t n) -{ - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_app_conf_t *dacf; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - return &ctx->frags[(ctx->frag + n) % (dacf->winfrags * 2 + 1)]; -} - - -static void -ngx_rtmp_dash_next_frag(ngx_rtmp_session_t *s) -{ - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_app_conf_t *dacf; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - if (ctx->nfrags == dacf->winfrags) { - ctx->frag++; - } else { - ctx->nfrags++; - } -} - - -static ngx_int_t -ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) -{ - /* rename file with overwrite */ - -#if (NGX_WIN32) - return MoveFileEx((LPCTSTR) src, (LPCTSTR) dst, MOVEFILE_REPLACE_EXISTING); -#else - return ngx_rename_file(src, dst); -#endif -} - - -static ngx_uint_t -ngx_rtmp_dash_gcd(ngx_uint_t m, ngx_uint_t n) -{ - /* greatest common divisor */ - - ngx_uint_t temp; - - while (n) { - temp=n; - n=m % n; - m=temp; - } - return m; -} - - -static ngx_int_t -ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) -{ - char *sep; - u_char *p, *last; - ssize_t n; - ngx_fd_t fd; - struct tm tm; - ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; - ngx_uint_t depth_msec, depth_sec; - ngx_uint_t update_period, update_period_msec; - ngx_uint_t buffer_time, buffer_time_msec; - ngx_uint_t presentation_delay, presentation_delay_msec; - ngx_uint_t gcd, par_x, par_y; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_rtmp_dash_frag_t *f; - ngx_rtmp_dash_app_conf_t *dacf; - ngx_rtmp_dash_variant_t *var; - ngx_str_t *arg; - - ngx_rtmp_playlist_t v; - - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; - static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; - static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; - static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); - - if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { - return NGX_ERROR; - } - - fd = ngx_open_file(ctx->var_playlist_bak.data, NGX_FILE_WRONLY, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - - if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: open failed: '%V'", &ctx->var_playlist_bak); - return NGX_ERROR; - } - - /** - * Availability time must be equal stream start time - * Cos segments time counting from it - */ - ngx_libc_gmtime(ctx->start_time.sec, &tm); - *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", - tm.tm_year + 1900, tm.tm_mon + 1, - tm.tm_mday, tm.tm_hour, - tm.tm_min, tm.tm_sec - ) = 0; - - /* Stream publish time */ - *ngx_sprintf(publish_time, "%s", avaliable_time) = 0; - - depth_sec = (ngx_uint_t) ( - ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + - ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration - - ngx_rtmp_dash_get_frag(s, 0)->timestamp); - - depth_msec = depth_sec % 1000; - depth_sec -= depth_msec; - depth_sec /= 1000; - - ngx_libc_gmtime(depth_sec, &tm); - - *ngx_sprintf(buffer_depth, "P%dY%02dM%02dDT%dH%02dM%02d.%03dS", - tm.tm_year - 70, tm.tm_mon, - tm.tm_mday - 1, tm.tm_hour, - tm.tm_min, tm.tm_sec, - depth_msec) = 0; - - last = buffer + sizeof(buffer); - - /** - * Calculate playlist minimal update period - * This should be more than biggest segment duration - * Cos segments rounded by keyframe/GOP. - * And that time not always equals to fragment length. - */ - update_period = dacf->fraglen; - - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - if (f->duration > update_period) { - update_period = f->duration; - } - } - - // Reasonable delay for streaming - presentation_delay = update_period * 2 + 1000; - presentation_delay_msec = presentation_delay % 1000; - presentation_delay -= presentation_delay_msec; - presentation_delay /= 1000; - - // Calculate msec part and seconds - update_period_msec = update_period % 1000; - update_period -= update_period_msec; - update_period /= 1000; - - // Buffer length by default fragment length - buffer_time = dacf->fraglen; - buffer_time_msec = buffer_time % 1000; - buffer_time -= buffer_time_msec; - buffer_time /= 1000; - - // Fill DASH header - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, - // availabilityStartTime - avaliable_time, - // publishTime - publish_time, - // minimumUpdatePeriod - update_period, update_period_msec, - // minBufferTime - buffer_time, buffer_time_msec, - // timeShiftBufferDepth - buffer_depth, - // suggestedPresentationDelay - presentation_delay, presentation_delay_msec - ); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_PERIOD); - - n = ngx_write_fd(fd, buffer, p - buffer); - - sep = (dacf->nested ? "/" : "-"); - var = dacf->variant->elts; - - if (ctx->has_video) { - frame_rate_num = (ngx_uint_t) (codec_ctx->frame_rate * 1000.); - - if (frame_rate_num % 1000 == 0) { - *ngx_sprintf(frame_rate, "%ui", frame_rate_num / 1000) = 0; - } else { - frame_rate_denom = 1000; - switch (frame_rate_num) { - case 23976: - frame_rate_num = 24000; - frame_rate_denom = 1001; - break; - case 29970: - frame_rate_num = 30000; - frame_rate_denom = 1001; - break; - case 59940: - frame_rate_num = 60000; - frame_rate_denom = 1001; - break; - } - - *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; - } - - gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height); - par_x = codec_ctx->width / gcd; - par_y = codec_ctx->height / gcd; - - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO, - codec_ctx->width, - codec_ctx->height, - frame_rate, - par_x, par_y); - - switch (dacf->ad_markers) { - case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: - case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: - p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); - } - - for (j = 0; j < dacf->variant->nelts; j++, var++) { - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, - &ctx->varname, &var->suffix, - codec_ctx->avc_profile, - codec_ctx->avc_compat, - codec_ctx->avc_level); - - arg = var->args.elts; - for (k = 0; k < var->args.nelts && k < 2 ; k++, arg++) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); - } - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO, - &ctx->varname, &var->suffix, sep, - &ctx->varname, &var->suffix, sep); - - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); - } - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); - } - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); - - n = ngx_write_fd(fd, buffer, p - buffer); - } - - if (ctx->has_audio) { - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, - &ctx->name, - codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? - (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", - codec_ctx->sample_rate, - (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), - &ctx->name, sep, - &ctx->name, sep); - - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); - } - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER); - - n = ngx_write_fd(fd, buffer, p - buffer); - } - - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER); - n = ngx_write_fd(fd, buffer, p - buffer); - - /* UTCTiming value */ - switch (dacf->clock_compensation) { - case NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP: - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, - "ntp", - &dacf->clock_helper_uri - ); - n = ngx_write_fd(fd, buffer, p - buffer); - break; - case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD: - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, - "http-head", - &dacf->clock_helper_uri - ); - n = ngx_write_fd(fd, buffer, p - buffer); - break; - case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO: - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, - "http-iso", - &dacf->clock_helper_uri - ); - n = ngx_write_fd(fd, buffer, p - buffer); - break; - } - - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER); - n = ngx_write_fd(fd, buffer, p - buffer); - - if (n < 0) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: write failed: '%V'", &ctx->var_playlist_bak); - ngx_close_file(fd); - return NGX_ERROR; - } - - ngx_close_file(fd); - - if (ngx_rtmp_dash_rename_file(ctx->var_playlist_bak.data, ctx->var_playlist.data) - == NGX_FILE_ERROR) - { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%V'->'%V'", - &ctx->var_playlist_bak, &ctx->var_playlist); - return NGX_ERROR; - } - - ngx_memzero(&v, sizeof(v)); - ngx_str_set(&(v.module), "dash"); - v.playlist.data = ctx->playlist.data; - v.playlist.len = ctx->playlist.len; - return next_playlist(s, &v); -} - - -static ngx_int_t -ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) -{ - char *sep; - u_char *p, *last; - ssize_t n; - ngx_fd_t fd, fds; - struct tm tm; - ngx_str_t noname, *name; - ngx_uint_t i, frame_rate_num, frame_rate_denom; - ngx_uint_t depth_msec, depth_sec; - ngx_uint_t update_period, update_period_msec; - ngx_uint_t buffer_time, buffer_time_msec; - ngx_uint_t presentation_delay, presentation_delay_msec; - ngx_uint_t gcd, par_x, par_y; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_rtmp_dash_frag_t *f; - ngx_rtmp_dash_app_conf_t *dacf; - - ngx_rtmp_playlist_t v; - - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; - static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; - static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; - static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); - - if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { - return NGX_ERROR; - } - - if (ctx->id == 0) { - ngx_rtmp_dash_write_init_segments(s); - } - - fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - - if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: open failed: '%V'", &ctx->playlist_bak); - return NGX_ERROR; - } - - /* write segments file */ - fds = ngx_open_file(ctx->segments_bak.data, NGX_FILE_WRONLY, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - - if (fds == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: open failed: '%V'", &ctx->segments_bak); - return NGX_ERROR; - } - - /** - * Availability time must be equal stream start time - * Cos segments time counting from it - */ - ngx_libc_gmtime(ctx->start_time.sec, &tm); - *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", - tm.tm_year + 1900, tm.tm_mon + 1, - tm.tm_mday, tm.tm_hour, - tm.tm_min, tm.tm_sec - ) = 0; - - /* Stream publish time */ - *ngx_sprintf(publish_time, "%s", avaliable_time) = 0; - - depth_sec = (ngx_uint_t) ( - ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + - ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration - - ngx_rtmp_dash_get_frag(s, 0)->timestamp); - - depth_msec = depth_sec % 1000; - depth_sec -= depth_msec; - depth_sec /= 1000; - - ngx_libc_gmtime(depth_sec, &tm); - - *ngx_sprintf(buffer_depth, "P%dY%02dM%02dDT%dH%02dM%02d.%03dS", - tm.tm_year - 70, tm.tm_mon, - tm.tm_mday - 1, tm.tm_hour, - tm.tm_min, tm.tm_sec, - depth_msec) = 0; - - last = buffer + sizeof(buffer); - - /** - * Calculate playlist minimal update period - * This should be more than biggest segment duration - * Cos segments rounded by keyframe/GOP. - * And that time not always equals to fragment length. - */ - update_period = dacf->fraglen; - - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - if (f->duration > update_period) { - update_period = f->duration; - } - } - - // Reasonable delay for streaming - presentation_delay = update_period * 2 + 1000; - presentation_delay_msec = presentation_delay % 1000; - presentation_delay -= presentation_delay_msec; - presentation_delay /= 1000; - - // Calculate msec part and seconds - update_period_msec = update_period % 1000; - update_period -= update_period_msec; - update_period /= 1000; - - // Buffer length by default fragment length - buffer_time = dacf->fraglen; - buffer_time_msec = buffer_time % 1000; - buffer_time -= buffer_time_msec; - buffer_time /= 1000; - - // Fill DASH header - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, - // availabilityStartTime - avaliable_time, - // publishTime - publish_time, - // minimumUpdatePeriod - update_period, update_period_msec, - // minBufferTime - buffer_time, buffer_time_msec, - // timeShiftBufferDepth - buffer_depth, - // suggestedPresentationDelay - presentation_delay, presentation_delay_msec - ); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_PERIOD); - - n = ngx_write_fd(fd, buffer, p - buffer); - - ngx_str_null(&noname); - - name = (dacf->nested ? &noname : &ctx->name); - sep = (dacf->nested ? "" : "-"); - - if (ctx->has_video) { - frame_rate_num = (ngx_uint_t) (codec_ctx->frame_rate * 1000.); - - if (frame_rate_num % 1000 == 0) { - *ngx_sprintf(frame_rate, "%ui", frame_rate_num / 1000) = 0; - } else { - frame_rate_denom = 1000; - switch (frame_rate_num) { - case 23976: - frame_rate_num = 24000; - frame_rate_denom = 1001; - break; - case 29970: - frame_rate_num = 30000; - frame_rate_denom = 1001; - break; - case 59940: - frame_rate_num = 60000; - frame_rate_denom = 1001; - break; - } - - *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; - } - - gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height); - par_x = codec_ctx->width / gcd; - par_y = codec_ctx->height / gcd; - - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO, - codec_ctx->width, - codec_ctx->height, - frame_rate, - par_x, par_y); - - switch (dacf->ad_markers) { - case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: - case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: - p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); - } - - n = ngx_write_fd(fd, buffer, p - buffer); - - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, - &ctx->name, - codec_ctx->avc_profile, - codec_ctx->avc_compat, - codec_ctx->avc_level, - codec_ctx->width, - codec_ctx->height, - frame_rate, - (ngx_uint_t) (codec_ctx->video_data_rate * 1000), - name, sep, - name, sep); - - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); - } - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); - - ngx_write_fd(fds, buffer, p - buffer); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); - - n = ngx_write_fd(fd, buffer, p - buffer); - } - - if (ctx->has_audio) { - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, - &ctx->name, - codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? - (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", - codec_ctx->sample_rate, - (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), - name, sep, - name, sep); - - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); - } - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER); - - n = ngx_write_fd(fd, buffer, p - buffer); - } - - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER); - n = ngx_write_fd(fd, buffer, p - buffer); - - /* UTCTiming value */ - switch (dacf->clock_compensation) { - case NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP: - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, - "ntp", - &dacf->clock_helper_uri - ); - n = ngx_write_fd(fd, buffer, p - buffer); - break; - case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD: - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, - "http-head", - &dacf->clock_helper_uri - ); - n = ngx_write_fd(fd, buffer, p - buffer); - break; - case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO: - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, - "http-iso", - &dacf->clock_helper_uri - ); - n = ngx_write_fd(fd, buffer, p - buffer); - break; - } - - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER); - n = ngx_write_fd(fd, buffer, p - buffer); - - if (n < 0) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: write failed: '%V'", &ctx->playlist_bak); - ngx_close_file(fd); - return NGX_ERROR; - } - - ngx_close_file(fd); - ngx_close_file(fds); - - if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data) - == NGX_FILE_ERROR) - { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%V'->'%V'", - &ctx->playlist_bak, &ctx->playlist); - return NGX_ERROR; - } - - if (ngx_rtmp_dash_rename_file(ctx->segments_bak.data, ctx->segments.data) - == NGX_FILE_ERROR) - { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%V'->'%V'", - &ctx->segments_bak, &ctx->segments); - return NGX_ERROR; - } - - /* try to write the variant file only once, check the max flag */ - if (ctx->var && ctx->var->args.nelts > 3) { - return ngx_rtmp_dash_write_variant_playlist(s); - } - - ngx_memzero(&v, sizeof(v)); - ngx_str_set(&(v.module), "dash"); - v.playlist.data = ctx->playlist.data; - v.playlist.len = ctx->playlist.len; - return next_playlist(s, &v); -} - - -static ngx_int_t -ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) -{ - ngx_fd_t fd; - ngx_int_t rc; - ngx_buf_t b; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); - - if (ctx == NULL || codec_ctx == NULL) { - return NGX_ERROR; - } - - /* init video */ - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4v") = 0; - - fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, - NGX_FILE_DEFAULT_ACCESS); - - if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error creating video init file"); - return NGX_ERROR; - } - - b.start = buffer; - b.end = b.start + sizeof(buffer); - b.pos = b.last = b.start; - - ngx_rtmp_mp4_write_ftyp(&b); - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK); - - rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); - if (rc == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: writing video init failed"); - } - - ngx_close_file(fd); - - /* init audio */ - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4a") = 0; - - fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, - NGX_FILE_DEFAULT_ACCESS); - - if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error creating dash audio init file"); - return NGX_ERROR; - } - - b.pos = b.last = b.start; - - ngx_rtmp_mp4_write_ftyp(&b); - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK); - - rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); - if (rc == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: writing audio init failed"); - } - - ngx_close_file(fd); - - return NGX_OK; -} - - -static void -ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) -{ - u_char *pos, *pos1; - size_t left; - ssize_t n; - ngx_fd_t fd; - ngx_buf_t b; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_frag_t *f; - - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - - if (!t->opened) { - return; - } - - ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: close fragment id=%ui, type=%c, pts=%uD", - t->id, t->type, t->earliest_pres_time); - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - b.start = buffer; - b.end = buffer + sizeof(buffer); - b.pos = b.last = b.start; - - if (ctx->has_cuepoint) { - - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint write emsg : epts='%ui', lpts='%ui', cpts='%ui', duration='%ui'", - t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_time, ctx->cuepoint_duration); - - /* ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, */ - ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ - ctx->cuepoint_time, - ctx->cuepoint_duration, - ctx->cuepoint_id); - - pos = b.last; - b.last = pos; - ctx->has_cuepoint = 0; - } - - ngx_rtmp_mp4_write_styp(&b); - - pos = b.last; - b.last += 44; /* leave room for sidx */ - - ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, - t->samples, t->sample_mask, t->id); - pos1 = b.last; - b.last = pos; - - ngx_rtmp_mp4_write_sidx(&b, t->mdat_size + 8 + (pos1 - (pos + 44)), - t->earliest_pres_time, t->latest_pres_time); - b.last = pos1; - ngx_rtmp_mp4_write_mdat(&b, t->mdat_size + 8); - - /* move the data down to make room for the headers */ - - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uD.m4%c", - f->timestamp, t->type) = 0; - - fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - - if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error creating dash temp video file"); - goto done; - } - - if (ngx_write_fd(fd, b.pos, (size_t) (b.last - b.pos)) == NGX_ERROR) { - goto done; - } - - left = (size_t) t->mdat_size; - -#if (NGX_WIN32) - if (SetFilePointer(t->fd, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: SetFilePointer error"); - goto done; - } -#else - if (lseek(t->fd, 0, SEEK_SET) == -1) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: lseek error"); - goto done; - } -#endif - - while (left > 0) { - - n = ngx_read_fd(t->fd, buffer, ngx_min(sizeof(buffer), left)); - if (n == NGX_ERROR) { - break; - } - - n = ngx_write_fd(fd, buffer, (size_t) n); - if (n == NGX_ERROR) { - break; - } - - left -= n; - } - -done: - - if (fd != NGX_INVALID_FILE) { - ngx_close_file(fd); - } - - ngx_close_file(t->fd); - - t->fd = NGX_INVALID_FILE; - t->opened = 0; -} - - -static ngx_int_t -ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) -{ - ngx_rtmp_dash_ctx_t *ctx; - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - if (ctx == NULL || !ctx->opened) { - return NGX_OK; - } - - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: close fragments"); - - ngx_rtmp_dash_close_fragment(s, &ctx->video); - ngx_rtmp_dash_close_fragment(s, &ctx->audio); - - ngx_rtmp_dash_next_frag(s); - - ngx_rtmp_dash_write_playlist(s); - - ctx->id++; - ctx->opened = 0; - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, - ngx_uint_t id, char type) -{ - ngx_rtmp_dash_ctx_t *ctx; - - if (t->opened) { - return NGX_OK; - } - - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: open fragment id=%ui, type='%c'", id, type); - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "raw.m4%c", type) = 0; - - t->fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - - if (t->fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error creating fragment file"); - return NGX_ERROR; - } - - t->id = id; - t->type = type; - t->sample_count = 0; - t->earliest_pres_time = 0; - t->latest_pres_time = 0; - t->mdat_size = 0; - t->opened = 1; - - if (type == 'v') { - t->sample_mask = NGX_RTMP_MP4_SAMPLE_SIZE| - NGX_RTMP_MP4_SAMPLE_DURATION| - NGX_RTMP_MP4_SAMPLE_DELAY| - NGX_RTMP_MP4_SAMPLE_KEY; - } else { - t->sample_mask = NGX_RTMP_MP4_SAMPLE_SIZE| - NGX_RTMP_MP4_SAMPLE_DURATION; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) -{ - ngx_rtmp_dash_ctx_t *ctx; - - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: open fragments"); - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - if (ctx->opened) { - return NGX_OK; - } - - if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) { - return NGX_ERROR; - } - - ngx_rtmp_dash_open_fragment(s, &ctx->video, ctx->id, 'v'); - - ngx_rtmp_dash_open_fragment(s, &ctx->audio, ctx->id, 'a'); - - ctx->opened = 1; - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s) -{ - size_t len; - ngx_file_info_t fi; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_app_conf_t *dacf; - - static u_char path[NGX_MAX_PATH + 1]; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - - *ngx_snprintf(path, sizeof(path) - 1, "%V", &dacf->path) = 0; - - if (ngx_file_info(path, &fi) == NGX_FILE_ERROR) { - - if (ngx_errno != NGX_ENOENT) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: " ngx_file_info_n " failed on '%V'", - &dacf->path); - return NGX_ERROR; - } - - /* ENOENT */ - - if (ngx_create_dir(path, NGX_RTMP_DASH_DIR_ACCESS) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: " ngx_create_dir_n " failed on '%V'", - &dacf->path); - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: directory '%V' created", &dacf->path); - - } else { - - if (!ngx_is_dir(&fi)) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: '%V' exists and is not a directory", - &dacf->path); - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: directory '%V' exists", &dacf->path); - } - - if (!dacf->nested) { - return NGX_OK; - } - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - len = dacf->path.len; - if (dacf->path.data[len - 1] == '/') { - len--; - } - - *ngx_snprintf(path, sizeof(path) - 1, "%*s/%V", len, dacf->path.data, - &ctx->name) = 0; - - if (ngx_file_info(path, &fi) != NGX_FILE_ERROR) { - - if (ngx_is_dir(&fi)) { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: directory '%s' exists", path); - return NGX_OK; - } - - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: '%s' exists and is not a directory", path); - - return NGX_ERROR; - } - - if (ngx_errno != NGX_ENOENT) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: " ngx_file_info_n " failed on '%s'", path); - return NGX_ERROR; - } - - /* NGX_ENOENT */ - - if (ngx_create_dir(path, NGX_RTMP_DASH_DIR_ACCESS) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: " ngx_create_dir_n " failed on '%s'", path); - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: directory '%s' created", path); - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) -{ - u_char *p, *pp; - size_t len; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_frag_t *f; - ngx_rtmp_dash_app_conf_t *dacf; - ngx_rtmp_dash_variant_t *var; - ngx_uint_t n; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { - goto next; - } - - if (s->auto_pushed) { - goto next; - } - - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: publish: name='%s' type='%s'", v->name, v->type); - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - if (ctx == NULL) { - ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_dash_ctx_t)); - if (ctx == NULL) { - goto next; - } - ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_dash_module); - - } else { - if (ctx->opened) { - goto next; - } - - f = ctx->frags; - ngx_memzero(ctx, sizeof(ngx_rtmp_dash_ctx_t)); - ctx->frags = f; - } - - if (ctx->frags == NULL) { - ctx->frags = ngx_pcalloc(s->connection->pool, - sizeof(ngx_rtmp_dash_frag_t) * - (dacf->winfrags * 2 + 1)); - if (ctx->frags == NULL) { - return NGX_ERROR; - } - } - - ctx->id = 0; - - if (ngx_strstr(v->name, "..")) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: bad stream name: '%s'", v->name); - return NGX_ERROR; - } - - ctx->name.len = ngx_strlen(v->name); - ctx->name.data = ngx_palloc(s->connection->pool, ctx->name.len + 1); - - if (ctx->name.data == NULL) { - return NGX_ERROR; - } - - *ngx_cpymem(ctx->name.data, v->name, ctx->name.len) = 0; - - len = dacf->path.len + 1 + ctx->name.len + sizeof(".mpd"); - if (dacf->nested) { - len += sizeof("/index") - 1; - } - - ctx->playlist.data = ngx_palloc(s->connection->pool, len); - p = ngx_cpymem(ctx->playlist.data, dacf->path.data, dacf->path.len); - - if (p[-1] != '/') { - *p++ = '/'; - } - - p = ngx_cpymem(p, ctx->name.data, ctx->name.len); - - /* - * ctx->stream holds initial part of stream file path - * however the space for the whole stream path - * is allocated - */ - - ctx->stream.len = p - ctx->playlist.data + 1; - ctx->stream.data = ngx_palloc(s->connection->pool, - ctx->stream.len + NGX_INT32_LEN + - sizeof(".m4x")); - - ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1); - ctx->stream.data[ctx->stream.len - 1] = (dacf->nested ? '/' : '-'); - - if (dacf->variant) { - var = dacf->variant->elts; - for (n = 0; n < dacf->variant->nelts; n++, var++) { - if (ctx->name.len > var->suffix.len && - ngx_memcmp(var->suffix.data, - ctx->name.data + ctx->name.len - var->suffix.len, - var->suffix.len) - == 0) - { - len = (size_t) (ctx->name.len - var->suffix.len); - - ctx->varname.len = len; - ctx->varname.data = ngx_palloc(s->connection->pool, - ctx->varname.len + 1); - pp = ngx_cpymem(ctx->varname.data, - ctx->name.data, len); - - *pp = 0; - - ctx->var = var; - - len = (size_t) (p - ctx->playlist.data); - - ctx->var_playlist.len = len - var->suffix.len + sizeof(".mpd") - -1; - ctx->var_playlist.data = ngx_palloc(s->connection->pool, - ctx->var_playlist.len + 1); - pp = ngx_cpymem(ctx->var_playlist.data, - ctx->playlist.data, len - var->suffix.len); - pp = ngx_cpymem(pp, ".mpd", sizeof(".mpd") - 1); - *pp = 0; - - ctx->var_playlist_bak.len = ctx->var_playlist.len + - sizeof(".bak") - 1; - ctx->var_playlist_bak.data = ngx_palloc(s->connection->pool, - ctx->var_playlist_bak.len + 1); - pp = ngx_cpymem(ctx->var_playlist_bak.data, - ctx->var_playlist.data, - ctx->var_playlist.len); - pp = ngx_cpymem(pp, ".bak", sizeof(".bak") - 1); - *pp = 0; - - break; - } - } - } - - if (dacf->nested) { - p = ngx_cpymem(p, "/index.mpd", sizeof("/index.mpd") - 1); - } else { - p = ngx_cpymem(p, ".mpd", sizeof(".mpd") - 1); - } - - ctx->playlist.len = p - ctx->playlist.data; - - *p = 0; - - /* playlist bak (new playlist) path */ - - ctx->playlist_bak.data = ngx_palloc(s->connection->pool, - ctx->playlist.len + sizeof(".bak")); - p = ngx_cpymem(ctx->playlist_bak.data, ctx->playlist.data, - ctx->playlist.len); - p = ngx_cpymem(p, ".bak", sizeof(".bak") - 1); - - ctx->playlist_bak.len = p - ctx->playlist_bak.data; - - *p = 0; - - /* segments path */ - - ctx->segments.data = ngx_palloc(s->connection->pool, - ctx->playlist.len - 4 + sizeof(".seg")); - p = ngx_cpymem(ctx->segments.data, ctx->playlist.data, - ctx->playlist.len - 4); - p = ngx_cpymem(p, ".seg", sizeof(".seg") - 1); - - ctx->segments.len = p - ctx->segments.data; - - *p = 0; - - /* segments bak (new segments) path */ - - ctx->segments_bak.data = ngx_palloc(s->connection->pool, - ctx->playlist.len - 4 + sizeof(".sbk")); - p = ngx_cpymem(ctx->segments_bak.data, ctx->playlist.data, - ctx->playlist.len - 4); - p = ngx_cpymem(p, ".sbk", sizeof(".sbk") - 1); - - ctx->segments_bak.len = p - ctx->segments_bak.data; - - *p = 0; - - ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: playlist='%V' playlist_bak='%V' segments='%V' segments_bak='%V' stream_pattern='%V'", - &ctx->playlist, &ctx->playlist_bak, &ctx->segments, &ctx->segments_bak, &ctx->stream); - - ctx->start_time = *ngx_cached_time; - - if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) { - return NGX_ERROR; - } - -next: - return next_publish(s, v); -} - - -static ngx_int_t -ngx_rtmp_dash_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) -{ - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_app_conf_t *dacf; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - if (dacf == NULL || !dacf->dash || ctx == NULL) { - goto next; - } - - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: delete stream"); - - ngx_rtmp_dash_close_fragments(s); - -next: - return next_close_stream(s, v); -} - -static void -ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, - uint32_t timestamp) -{ - int32_t d; - ngx_int_t hit; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_frag_t *f; - ngx_rtmp_dash_app_conf_t *dacf; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); - - ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: timestamp=%ui, f-timestamp=%ui, boundary=%i, dacf-fraglen=%ui", - timestamp, f->timestamp, boundary, dacf->fraglen); - - d = (int32_t) (timestamp - f->timestamp); - - if (d >= 0) { - - f->duration = timestamp - f->timestamp; - hit = (f->duration >= dacf->fraglen); - - } else { - - /* sometimes clients generate slightly unordered frames */ - - hit = (-d > 1000); - } - - ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: d=%i, f-duration=%ui, hit=%i", - d, f->duration, hit); - - if (ctx->has_video && !hit) { - boundary = 0; - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: boundary=0 cos has_video && !hit"); - } - - if (!ctx->has_video && ctx->has_audio) { - boundary = hit; - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: boundary=hit cos !has_video && has_audio"); - } - - if (ctx->audio.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) { - boundary = 1; - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: boundary=1 cos audio max mdat"); - } - - if (ctx->video.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) { - boundary = 1; - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: boundary=1 cos video max mdat"); - } - - if (!ctx->opened) { - boundary = 1; - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: boundary=1 cos !opened"); - } - - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: update_fragments: boundary=%i", - boundary); - - if (boundary) { - ngx_rtmp_dash_close_fragments(s); - ngx_rtmp_dash_open_fragments(s); - - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); - f->timestamp = timestamp; - } -} - - -static ngx_int_t -ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, - ngx_rtmp_dash_track_t *t, ngx_int_t key, uint32_t timestamp, uint32_t delay) -{ - u_char *p; - size_t size, bsize; - ngx_rtmp_mp4_sample_t *smpl; - - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - - p = buffer; - size = 0; - - for (; in && size < sizeof(buffer); in = in->next) { - - bsize = (size_t) (in->buf->last - in->buf->pos); - if (size + bsize > sizeof(buffer)) { - bsize = (size_t) (sizeof(buffer) - size); - } - - p = ngx_cpymem(p, in->buf->pos, bsize); - size += bsize; - } - - ngx_rtmp_dash_update_fragments(s, key, timestamp); - - if (t->sample_count == 0) { - t->earliest_pres_time = timestamp; - } - - t->latest_pres_time = timestamp; - - if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { - - if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: " ngx_write_fd_n " failed"); - return NGX_ERROR; - } - - smpl = &t->samples[t->sample_count]; - - smpl->delay = delay; - smpl->size = (uint32_t) size; - smpl->duration = 0; - smpl->timestamp = timestamp; - smpl->key = (key ? 1 : 0); - - if (t->sample_count > 0) { - smpl = &t->samples[t->sample_count - 1]; - smpl->duration = timestamp - smpl->timestamp; - } - - t->sample_count++; - t->mdat_size += (ngx_uint_t) size; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) -{ - u_char htype; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_rtmp_dash_app_conf_t *dacf; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); - - if (dacf == NULL || !dacf->dash || ctx == NULL || - codec_ctx == NULL || h->mlen < 2) - { - return NGX_OK; - } - - /* Only AAC is supported */ - - if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC || - codec_ctx->aac_header == NULL) - { - return NGX_OK; - } - - if (in->buf->last - in->buf->pos < 2) { - return NGX_ERROR; - } - - /* skip AAC config */ - - htype = in->buf->pos[1]; - if (htype != 1) { - return NGX_OK; - } - - ctx->has_audio = 1; - - /* skip RTMP & AAC headers */ - - in->buf->pos += 2; - - return ngx_rtmp_dash_append(s, in, &ctx->audio, 0, h->timestamp, 0); -} - - -static ngx_int_t -ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) -{ - u_char *p; - uint8_t ftype, htype; - uint32_t delay; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_rtmp_dash_app_conf_t *dacf; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); - - if (dacf == NULL || !dacf->dash || ctx == NULL || codec_ctx == NULL || - codec_ctx->avc_header == NULL || h->mlen < 5) - { - return NGX_OK; - } - - /* Only H264 is supported */ - - if (codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) { - return NGX_OK; - } - - if (in->buf->last - in->buf->pos < 5) { - return NGX_ERROR; - } - - ftype = (in->buf->pos[0] & 0xf0) >> 4; - - /* skip AVC config */ - - htype = in->buf->pos[1]; - if (htype != 1) { - return NGX_OK; - } - - p = (u_char *) &delay; - - p[0] = in->buf->pos[4]; - p[1] = in->buf->pos[3]; - p[2] = in->buf->pos[2]; - p[3] = 0; - - ctx->has_video = 1; - - /* skip RTMP & H264 headers */ - - in->buf->pos += 5; - - return ngx_rtmp_dash_append(s, in, &ctx->video, ftype == 1, h->timestamp, - delay); -} - - -static ngx_int_t -ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) -{ - return next_stream_begin(s, v); -} - - -static ngx_int_t -ngx_rtmp_dash_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v) -{ - ngx_rtmp_dash_close_fragments(s); - - return next_stream_eof(s, v); -} - - -static ngx_int_t -ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) -{ - time_t mtime, max_age; - u_char *p; - u_char path[NGX_MAX_PATH + 1], mpd_path[NGX_MAX_PATH + 1]; - ngx_dir_t dir; - ngx_err_t err; - ngx_str_t name, spath, mpd; - ngx_int_t nentries, nerased; - ngx_file_info_t fi; - - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, - "dash: cleanup path='%V' playlen=%M", ppath, playlen); - - if (ngx_open_dir(ppath, &dir) != NGX_OK) { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, ngx_errno, - "dash: cleanup open dir failed '%V'", ppath); - return NGX_ERROR; - } - - nentries = 0; - nerased = 0; - - for ( ;; ) { - ngx_set_errno(0); - - if (ngx_read_dir(&dir) == NGX_ERROR) { - err = ngx_errno; - - if (ngx_close_dir(&dir) == NGX_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, - "dash: cleanup " ngx_close_dir_n " \"%V\" failed", - ppath); - } - - if (err == NGX_ENOMOREFILES) { - return nentries - nerased; - } - - ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, err, - "dash: cleanup " ngx_read_dir_n - " '%V' failed", ppath); - return NGX_ERROR; - } - - name.data = ngx_de_name(&dir); - if (name.data[0] == '.') { - continue; - } - - name.len = ngx_de_namelen(&dir); - - p = ngx_snprintf(path, sizeof(path) - 1, "%V/%V", ppath, &name); - *p = 0; - - spath.data = path; - spath.len = p - path; - - nentries++; - - if (!dir.valid_info && ngx_de_info(path, &dir) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, - "dash: cleanup " ngx_de_info_n " \"%V\" failed", - &spath); - - continue; - } - - if (ngx_de_is_dir(&dir)) { - - if (ngx_rtmp_dash_cleanup_dir(&spath, playlen) == 0) { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, - "dash: cleanup dir '%V'", &name); - - /* - * null-termination gets spoiled in win32 - * version of ngx_open_dir - */ - - *p = 0; - - if (ngx_delete_dir(path) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "dash: cleanup " ngx_delete_dir_n - " failed on '%V'", &spath); - } else { - nerased++; - } - } - - continue; - } - - if (!ngx_de_is_file(&dir)) { - continue; - } - - if (name.len >= 8 && name.data[name.len - 8] == 'i' && - name.data[name.len - 7] == 'n' && - name.data[name.len - 6] == 'i' && - name.data[name.len - 5] == 't' && - name.data[name.len - 4] == '.' && - name.data[name.len - 3] == 'm' && - name.data[name.len - 2] == '4') - { - if (name.len == 8) { - ngx_str_set(&mpd, "index"); - } else { - mpd.data = name.data; - mpd.len = name.len - 9; - } - - p = ngx_snprintf(mpd_path, sizeof(mpd_path) - 1, "%V/%V.mpd", - ppath, &mpd); - *p = 0; - - if (ngx_file_info(mpd_path, &fi) != NGX_FILE_ERROR) { - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, - "dash: cleanup '%V' delayed, mpd exists '%s'", - &name, mpd_path); - continue; - } - - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, - "dash: cleanup '%V' allowed, mpd missing '%s'", - &name, mpd_path); - - max_age = 0; - - } else if (name.len >= 4 && name.data[name.len - 4] == '.' && - name.data[name.len - 3] == 'm' && - name.data[name.len - 2] == '4' && - name.data[name.len - 1] == 'v') - { - max_age = playlen / 500; - - } else if (name.len >= 4 && name.data[name.len - 4] == '.' && - name.data[name.len - 3] == 'm' && - name.data[name.len - 2] == '4' && - name.data[name.len - 1] == 'a') - { - max_age = playlen / 500; - - } else if (name.len >= 4 && name.data[name.len - 4] == '.' && - name.data[name.len - 3] == 'm' && - name.data[name.len - 2] == 'p' && - name.data[name.len - 1] == 'd') - { - max_age = playlen / 500; - - } else if (name.len >= 4 && name.data[name.len - 4] == '.' && - name.data[name.len - 3] == 'r' && - name.data[name.len - 2] == 'a' && - name.data[name.len - 1] == 'w') - { - max_age = playlen / 500; - - } else { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, - "dash: cleanup skip unknown file type '%V'", &name); - continue; - } - - mtime = ngx_de_mtime(&dir); - if (mtime + max_age > ngx_cached_time->sec) { - continue; - } - - ngx_log_debug3(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, - "dash: cleanup '%V' mtime=%T age=%T", - &name, mtime, ngx_cached_time->sec - mtime); - - if (ngx_delete_file(path) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "dash: cleanup " ngx_delete_file_n " failed on '%V'", - &spath); - continue; - } - - nerased++; - } -} - - -#if (nginx_version >= 1011005) -static ngx_msec_t -#else -static time_t -#endif -ngx_rtmp_dash_cleanup(void *data) -{ - ngx_rtmp_dash_cleanup_t *cleanup = data; - - ngx_rtmp_dash_cleanup_dir(&cleanup->path, cleanup->playlen); - - // Next callback in doubled playlist length time to make sure what all - // players read all segments -#if (nginx_version >= 1011005) - return cleanup->playlen * 2; -#else - return cleanup->playlen / 500; -#endif -} - - -static char * -ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ - ngx_rtmp_dash_app_conf_t *dacf = conf; - - ngx_str_t *value, *arg; - ngx_uint_t n; - ngx_rtmp_dash_variant_t *var; - - value = cf->args->elts; - - if (dacf->variant == NULL) { - dacf->variant = ngx_array_create(cf->pool, 1, - sizeof(ngx_rtmp_dash_variant_t)); - if (dacf->variant == NULL) { - return NGX_CONF_ERROR; - } - } - - var = ngx_array_push(dacf->variant); - if (var == NULL) { - return NGX_CONF_ERROR; - } - - ngx_memzero(var, sizeof(ngx_rtmp_dash_variant_t)); - - var->suffix = value[1]; - - if (cf->args->nelts == 2) { - return NGX_CONF_OK; - } - - if (ngx_array_init(&var->args, cf->pool, cf->args->nelts - 2, - sizeof(ngx_str_t)) - != NGX_OK) - { - return NGX_CONF_ERROR; - } - - arg = ngx_array_push_n(&var->args, cf->args->nelts - 2); - if (arg == NULL) { - return NGX_CONF_ERROR; - } - - for (n = 2; n < cf->args->nelts; n++) { - *arg++ = value[n]; - } - - return NGX_CONF_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v) -{ - return next_playlist(s, v); -} - - -static ngx_int_t -ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) -{ - ngx_int_t res; - ngx_rtmp_dash_ctx_t *ctx; - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - static struct { - double time; - double duration; - u_char name[128]; - u_char type[128]; - u_char ptype[128]; - } v; - - static ngx_rtmp_amf_elt_t in_pr_elts[] = { - - { NGX_RTMP_AMF_STRING, - ngx_string("type"), - v.ptype, sizeof(v.ptype) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("duration"), - &v.duration, sizeof(v.duration) }, - - }; - - static ngx_rtmp_amf_elt_t in_dt_elts[] = { - - { NGX_RTMP_AMF_NUMBER, - ngx_string("time"), - &v.time, sizeof(v.time) }, - - { NGX_RTMP_AMF_STRING, - ngx_string("name"), - v.name, sizeof(v.name) }, - - { NGX_RTMP_AMF_STRING, - ngx_string("type"), - v.type, sizeof(v.type) }, - - { NGX_RTMP_AMF_OBJECT, - ngx_string("parameters"), - in_pr_elts, sizeof(in_pr_elts) }, - - }; - - static ngx_rtmp_amf_elt_t in_elts[] = { - - { NGX_RTMP_AMF_OBJECT, - ngx_null_string, - in_dt_elts, sizeof(in_dt_elts) }, - - }; - - ngx_memzero(&v, sizeof(v)); - res = ngx_rtmp_receive_amf(s, in, in_elts, - sizeof(in_elts) / sizeof(in_elts[0])); - - if (res == NGX_OK) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'", - h->timestamp, v.time, v.name, v.type, v.ptype, v.duration); - } else { - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash : onCuepoint : amf not understood"); - } - - ctx->has_cuepoint = 1; - ctx->cuepoint_time = h->timestamp; - ctx->cuepoint_duration = v.duration; - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) -{ - ngx_int_t res; - ngx_rtmp_dash_ctx_t *ctx; - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - - static struct { - unsigned ooni; - unsigned splice_event_ci; - unsigned splice_imd; - double avail_num; - double avail_expected; - double duration; - double prtime; - double prgid; - double sctype; - double sevtid; - u_char type[128]; - u_char mtype[128]; - } v; - - static ngx_rtmp_amf_elt_t in_pr_elts[] = { - - { NGX_RTMP_AMF_STRING, - ngx_string("messageType"), - v.mtype, sizeof(v.mtype) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("splice_command_type"), - &v.sctype, sizeof(v.sctype) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("splice_event_id"), - &v.sevtid, sizeof(v.sevtid) }, - - { NGX_RTMP_AMF_BOOLEAN, - ngx_string("splice_event_cancel_indicator"), - &v.splice_event_ci, sizeof(v.splice_event_ci) }, - - { NGX_RTMP_AMF_BOOLEAN, - ngx_string("out_of_network_indicator"), - &v.ooni, sizeof(v.ooni) }, - - { NGX_RTMP_AMF_BOOLEAN, - ngx_string("splice_immediate"), - &v.splice_imd, sizeof(v.splice_imd) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("pre_roll_time"), - &v.prtime, sizeof(v.prtime) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("break_duration"), - &v.duration, sizeof(v.duration) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("unique_program_id"), - &v.prgid, sizeof(v.prgid) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("avail_num"), - &v.avail_num, sizeof(v.avail_num) }, - - { NGX_RTMP_AMF_NUMBER, - ngx_string("avail_expected"), - &v.avail_expected, sizeof(v.avail_expected) }, - - }; - - static ngx_rtmp_amf_elt_t in_dt_elts[] = { - - { NGX_RTMP_AMF_STRING, - ngx_string("type"), - v.type, sizeof(v.type) }, - - { NGX_RTMP_AMF_OBJECT, - ngx_string("parameters"), - in_pr_elts, sizeof(in_pr_elts) }, - - }; - - static ngx_rtmp_amf_elt_t in_elts[] = { - - { NGX_RTMP_AMF_OBJECT, - ngx_null_string, - in_dt_elts, sizeof(in_dt_elts) }, - - }; - - ngx_memzero(&v, sizeof(v)); - res = ngx_rtmp_receive_amf(s, in, in_elts, - sizeof(in_elts) / sizeof(in_elts[0])); - - if (res == NGX_OK && v.duration > 0) { - ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint_scte35 : ts='%ui', type='%s', mtype='%s', sctype='%f', "\ - "scid='%f', prgid='%f', duration='%f', avail_num='%f', avail_expected='%f'", - h->timestamp, v.type, v.mtype, v.sctype, - v.sevtid, v.prgid, v.duration, v.avail_num, v.avail_expected); - } else { - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash : onCuepoint_scte35 : amf not understood"); - } - - if (v.duration > 0) { - ctx->has_cuepoint = 1; - ctx->cuepoint_time = h->timestamp; - ctx->cuepoint_duration = v.duration; - ctx->cuepoint_id = v.prgid; - } - - return NGX_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_ad_markers(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) -{ - ngx_rtmp_dash_app_conf_t *dacf; - - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { - return NGX_OK; - } - - switch (dacf->ad_markers) { - case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: - return ngx_rtmp_dash_on_cuepoint(s, h, in); - break; - case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: - return ngx_rtmp_dash_on_cuepoint_scte35(s, h, in); - break; - default: - return NGX_OK; - } - - return NGX_OK; -} - - -static void * -ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) -{ - ngx_rtmp_dash_app_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_dash_app_conf_t)); - if (conf == NULL) { - return NULL; - } - - conf->dash = NGX_CONF_UNSET; - conf->fraglen = NGX_CONF_UNSET_MSEC; - conf->playlen = NGX_CONF_UNSET_MSEC; - conf->cleanup = NGX_CONF_UNSET; - conf->nested = NGX_CONF_UNSET; - conf->clock_compensation = NGX_CONF_UNSET; - conf->ad_markers = NGX_CONF_UNSET; - - return conf; -} - - -static char * -ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_rtmp_dash_app_conf_t *prev = parent; - ngx_rtmp_dash_app_conf_t *conf = child; - ngx_rtmp_dash_cleanup_t *cleanup; - - ngx_conf_merge_value(conf->dash, prev->dash, 0); - ngx_conf_merge_msec_value(conf->fraglen, prev->fraglen, 5000); - ngx_conf_merge_msec_value(conf->playlen, prev->playlen, 30000); - ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1); - ngx_conf_merge_value(conf->nested, prev->nested, 0); - ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, - NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); - ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); - ngx_conf_merge_uint_value(conf->ad_markers, prev->ad_markers, - NGX_RTMP_DASH_AD_MARKERS_OFF); - - if (conf->fraglen) { - conf->winfrags = conf->playlen / conf->fraglen; - } - - /* schedule cleanup */ - - if (conf->dash && conf->path.len && conf->cleanup) { - if (conf->path.data[conf->path.len - 1] == '/') { - conf->path.len--; - } - - cleanup = ngx_pcalloc(cf->pool, sizeof(*cleanup)); - if (cleanup == NULL) { - return NGX_CONF_ERROR; - } - - cleanup->path = conf->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_dash_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, ""); - - return NGX_CONF_OK; -} - - -static ngx_int_t -ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) -{ - ngx_rtmp_handler_pt *h; - ngx_rtmp_core_main_conf_t *cmcf; - 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_VIDEO]); - *h = ngx_rtmp_dash_video; - - h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]); - *h = ngx_rtmp_dash_audio; - - next_publish = ngx_rtmp_publish; - ngx_rtmp_publish = ngx_rtmp_dash_publish; - - next_close_stream = ngx_rtmp_close_stream; - ngx_rtmp_close_stream = ngx_rtmp_dash_close_stream; - - next_stream_begin = ngx_rtmp_stream_begin; - ngx_rtmp_stream_begin = ngx_rtmp_dash_stream_begin; - - next_stream_eof = ngx_rtmp_stream_eof; - ngx_rtmp_stream_eof = ngx_rtmp_dash_stream_eof; - - next_playlist = ngx_rtmp_playlist; - ngx_rtmp_playlist = ngx_rtmp_dash_playlist; - - ch = ngx_array_push(&cmcf->amf); - ngx_str_set(&ch->name, "onCuePoint"); - ch->handler = ngx_rtmp_dash_ad_markers; - - return NGX_OK; -} From 128d4fd62a1a4aaf729724bbd8833a7984b6c002 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 30 Jun 2017 13:21:33 +0000 Subject: [PATCH 036/113] OK same file for now :/ --- dash/ngx_rtmp_dash_module.c | 2412 +++++++++++++++++++++++++++++++++++ 1 file changed, 2412 insertions(+) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e69de29bb..e5cfd15b9 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -0,0 +1,2412 @@ + + +#include +#include +#include +#include +#include "ngx_rtmp_live_module.h" +#include "ngx_rtmp_mp4.h" +#include "ngx_rtmp_dash_templates.h" + + +static ngx_rtmp_publish_pt next_publish; +static ngx_rtmp_close_stream_pt next_close_stream; +static ngx_rtmp_stream_begin_pt next_stream_begin; +static ngx_rtmp_stream_eof_pt next_stream_eof; +static ngx_rtmp_playlist_pt next_playlist; + + +static char * ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); +static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf); +static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf); +static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, + void *parent, void *child); +static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s); +static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s); + + +#define NGX_RTMP_DASH_BUFSIZE (1024*1024) +#define NGX_RTMP_DASH_MAX_MDAT (10*1024*1024) +#define NGX_RTMP_DASH_MAX_SAMPLES 1024 +#define NGX_RTMP_DASH_DIR_ACCESS 0744 + +#define NGX_RTMP_DASH_GMT_LENGTH sizeof("1970-09-28T12:00:00+06:00") + +typedef struct { + uint32_t timestamp; + uint32_t duration; +} ngx_rtmp_dash_frag_t; + + +typedef struct { + ngx_uint_t id; + ngx_uint_t opened; + ngx_uint_t mdat_size; + ngx_uint_t sample_count; + ngx_uint_t sample_mask; + ngx_fd_t fd; + char type; + uint32_t earliest_pres_time; + uint32_t latest_pres_time; + ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; +} ngx_rtmp_dash_track_t; + + +typedef struct { + ngx_str_t suffix; + ngx_array_t args; +} ngx_rtmp_dash_variant_t; + + +typedef struct { + ngx_str_t segments; + ngx_str_t segments_bak; + ngx_str_t playlist; + ngx_str_t playlist_bak; + ngx_str_t var_playlist; + ngx_str_t var_playlist_bak; + ngx_str_t name; + ngx_str_t varname; + ngx_str_t stream; + ngx_time_t start_time; + + ngx_uint_t nfrags; + ngx_uint_t frag; + ngx_rtmp_dash_frag_t *frags; /* circular 2 * winfrags + 1 */ + + unsigned opened:1; + unsigned has_video:1; + unsigned has_audio:1; + unsigned has_cuepoint:1; + + uint32_t cuepoint_time; + uint32_t cuepoint_duration; + uint32_t cuepoint_id; + + ngx_file_t video_file; + ngx_file_t audio_file; + + ngx_uint_t id; + + ngx_rtmp_dash_track_t audio; + ngx_rtmp_dash_track_t video; + ngx_rtmp_dash_variant_t *var; + +} ngx_rtmp_dash_ctx_t; + + +typedef struct { + ngx_str_t path; + ngx_msec_t playlen; +} ngx_rtmp_dash_cleanup_t; + + +#define NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF 1 +#define NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP 2 +#define NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD 3 +#define NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO 4 + +static ngx_conf_enum_t ngx_rtmp_dash_clock_compensation_type_slots[] = { + { ngx_string("off"), NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF }, + { ngx_string("ntp"), NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP }, + { ngx_string("http_head"), NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD }, + { ngx_string("http_iso"), NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO }, + { ngx_null_string, 0 } +}; + +#define NGX_RTMP_DASH_AD_MARKERS_OFF 1 +#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT 2 +#define NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 3 + +static ngx_conf_enum_t ngx_rtmp_dash_ad_markers_type_slots[] = { + { ngx_string("off"), NGX_RTMP_DASH_AD_MARKERS_OFF }, + { ngx_string("on_cuepoint"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT }, + { ngx_string("on_cuepoint_scte35"), NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35 }, + { ngx_null_string, 0 } +}; + +typedef struct { + ngx_flag_t dash; + ngx_msec_t fraglen; + ngx_msec_t playlen; + ngx_flag_t nested; + ngx_uint_t clock_compensation; // Try to compensate clock drift + // between client and server (on client side) + ngx_str_t clock_helper_uri; // Use uri to static file on HTTP server + // - same machine as RTMP/DASH) + // - or NTP server address + ngx_str_t path; + ngx_uint_t winfrags; + ngx_flag_t cleanup; + ngx_path_t *slot; + ngx_array_t *variant; + ngx_uint_t ad_markers; +} ngx_rtmp_dash_app_conf_t; + + +static ngx_command_t ngx_rtmp_dash_commands[] = { + + { ngx_string("dash"), + 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_dash_app_conf_t, dash), + NULL }, + + { ngx_string("dash_fragment"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_RTMP_APP_CONF_OFFSET, + offsetof(ngx_rtmp_dash_app_conf_t, fraglen), + NULL }, + + { ngx_string("dash_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_dash_app_conf_t, path), + NULL }, + + { ngx_string("dash_playlist_length"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, + ngx_conf_set_msec_slot, + NGX_RTMP_APP_CONF_OFFSET, + offsetof(ngx_rtmp_dash_app_conf_t, playlen), + NULL }, + + { ngx_string("dash_cleanup"), + 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_dash_app_conf_t, cleanup), + NULL }, + + { ngx_string("dash_nested"), + 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_dash_app_conf_t, nested), + NULL }, + + { ngx_string("dash_clock_compensation"), + 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_dash_app_conf_t, clock_compensation), + &ngx_rtmp_dash_clock_compensation_type_slots }, + + { ngx_string("dash_clock_helper_uri"), + 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_dash_app_conf_t, clock_helper_uri), + NULL }, + + { ngx_string("dash_variant"), + NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE, + ngx_rtmp_dash_variant, + NGX_RTMP_APP_CONF_OFFSET, + 0, + NULL }, + + { ngx_string("dash_ad_markers"), + 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_dash_app_conf_t, ad_markers), + &ngx_rtmp_dash_ad_markers_type_slots }, + + ngx_null_command +}; + + +static ngx_rtmp_module_t ngx_rtmp_dash_module_ctx = { + NULL, /* preconfiguration */ + ngx_rtmp_dash_postconfiguration, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + NULL, /* create server configuration */ + NULL, /* merge server configuration */ + + ngx_rtmp_dash_create_app_conf, /* create location configuration */ + ngx_rtmp_dash_merge_app_conf, /* merge location configuration */ +}; + + +ngx_module_t ngx_rtmp_dash_module = { + NGX_MODULE_V1, + &ngx_rtmp_dash_module_ctx, /* module context */ + ngx_rtmp_dash_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 ngx_rtmp_dash_frag_t * +ngx_rtmp_dash_get_frag(ngx_rtmp_session_t *s, ngx_int_t n) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + return &ctx->frags[(ctx->frag + n) % (dacf->winfrags * 2 + 1)]; +} + + +static void +ngx_rtmp_dash_next_frag(ngx_rtmp_session_t *s) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + if (ctx->nfrags == dacf->winfrags) { + ctx->frag++; + } else { + ctx->nfrags++; + } +} + + +static ngx_int_t +ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) +{ + /* rename file with overwrite */ + +#if (NGX_WIN32) + return MoveFileEx((LPCTSTR) src, (LPCTSTR) dst, MOVEFILE_REPLACE_EXISTING); +#else + return ngx_rename_file(src, dst); +#endif +} + + +static ngx_uint_t +ngx_rtmp_dash_gcd(ngx_uint_t m, ngx_uint_t n) +{ + /* greatest common divisor */ + + ngx_uint_t temp; + + while (n) { + temp=n; + n=m % n; + m=temp; + } + return m; +} + + +static ngx_int_t +ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) +{ + char *sep; + u_char *p, *last; + ssize_t n; + ngx_fd_t fd; + struct tm tm; + ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; + ngx_uint_t depth_msec, depth_sec; + ngx_uint_t update_period, update_period_msec; + ngx_uint_t buffer_time, buffer_time_msec; + ngx_uint_t presentation_delay, presentation_delay_msec; + ngx_uint_t gcd, par_x, par_y; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_frag_t *f; + ngx_rtmp_dash_app_conf_t *dacf; + ngx_rtmp_dash_variant_t *var; + ngx_str_t *arg; + + ngx_rtmp_playlist_t v; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; + static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { + return NGX_ERROR; + } + + fd = ngx_open_file(ctx->var_playlist_bak.data, NGX_FILE_WRONLY, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: open failed: '%V'", &ctx->var_playlist_bak); + return NGX_ERROR; + } + + /** + * Availability time must be equal stream start time + * Cos segments time counting from it + */ + ngx_libc_gmtime(ctx->start_time.sec, &tm); + *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", + tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec + ) = 0; + + /* Stream publish time */ + *ngx_sprintf(publish_time, "%s", avaliable_time) = 0; + + depth_sec = (ngx_uint_t) ( + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration - + ngx_rtmp_dash_get_frag(s, 0)->timestamp); + + depth_msec = depth_sec % 1000; + depth_sec -= depth_msec; + depth_sec /= 1000; + + ngx_libc_gmtime(depth_sec, &tm); + + *ngx_sprintf(buffer_depth, "P%dY%02dM%02dDT%dH%02dM%02d.%03dS", + tm.tm_year - 70, tm.tm_mon, + tm.tm_mday - 1, tm.tm_hour, + tm.tm_min, tm.tm_sec, + depth_msec) = 0; + + last = buffer + sizeof(buffer); + + /** + * Calculate playlist minimal update period + * This should be more than biggest segment duration + * Cos segments rounded by keyframe/GOP. + * And that time not always equals to fragment length. + */ + update_period = dacf->fraglen; + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + if (f->duration > update_period) { + update_period = f->duration; + } + } + + // Reasonable delay for streaming + presentation_delay = update_period * 2 + 1000; + presentation_delay_msec = presentation_delay % 1000; + presentation_delay -= presentation_delay_msec; + presentation_delay /= 1000; + + // Calculate msec part and seconds + update_period_msec = update_period % 1000; + update_period -= update_period_msec; + update_period /= 1000; + + // Buffer length by default fragment length + buffer_time = dacf->fraglen; + buffer_time_msec = buffer_time % 1000; + buffer_time -= buffer_time_msec; + buffer_time /= 1000; + + // Fill DASH header + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, + // availabilityStartTime + avaliable_time, + // publishTime + publish_time, + // minimumUpdatePeriod + update_period, update_period_msec, + // minBufferTime + buffer_time, buffer_time_msec, + // timeShiftBufferDepth + buffer_depth, + // suggestedPresentationDelay + presentation_delay, presentation_delay_msec + ); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_PERIOD); + + n = ngx_write_fd(fd, buffer, p - buffer); + + sep = (dacf->nested ? "/" : "-"); + var = dacf->variant->elts; + + if (ctx->has_video) { + frame_rate_num = (ngx_uint_t) (codec_ctx->frame_rate * 1000.); + + if (frame_rate_num % 1000 == 0) { + *ngx_sprintf(frame_rate, "%ui", frame_rate_num / 1000) = 0; + } else { + frame_rate_denom = 1000; + switch (frame_rate_num) { + case 23976: + frame_rate_num = 24000; + frame_rate_denom = 1001; + break; + case 29970: + frame_rate_num = 30000; + frame_rate_denom = 1001; + break; + case 59940: + frame_rate_num = 60000; + frame_rate_denom = 1001; + break; + } + + *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; + } + + gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height); + par_x = codec_ctx->width / gcd; + par_y = codec_ctx->height / gcd; + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO, + codec_ctx->width, + codec_ctx->height, + frame_rate, + par_x, par_y); + + switch (dacf->ad_markers) { + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: + p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); + } + + for (j = 0; j < dacf->variant->nelts; j++, var++) { + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, + &ctx->varname, &var->suffix, + codec_ctx->avc_profile, + codec_ctx->avc_compat, + codec_ctx->avc_level); + + arg = var->args.elts; + for (k = 0; k < var->args.nelts && k < 2 ; k++, arg++) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO, + &ctx->varname, &var->suffix, sep, + &ctx->varname, &var->suffix, sep); + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + f->timestamp, f->duration); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); + + n = ngx_write_fd(fd, buffer, p - buffer); + } + + if (ctx->has_audio) { + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, + &ctx->name, + codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? + (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", + codec_ctx->sample_rate, + (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), + &ctx->name, sep, + &ctx->name, sep); + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + f->timestamp, f->duration); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER); + + n = ngx_write_fd(fd, buffer, p - buffer); + } + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); + + /* UTCTiming value */ + switch (dacf->clock_compensation) { + case NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "ntp", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "http-head", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "http-iso", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + } + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); + + if (n < 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: write failed: '%V'", &ctx->var_playlist_bak); + ngx_close_file(fd); + return NGX_ERROR; + } + + ngx_close_file(fd); + + if (ngx_rtmp_dash_rename_file(ctx->var_playlist_bak.data, ctx->var_playlist.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%V'->'%V'", + &ctx->var_playlist_bak, &ctx->var_playlist); + return NGX_ERROR; + } + + ngx_memzero(&v, sizeof(v)); + ngx_str_set(&(v.module), "dash"); + v.playlist.data = ctx->playlist.data; + v.playlist.len = ctx->playlist.len; + return next_playlist(s, &v); +} + + +static ngx_int_t +ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) +{ + char *sep; + u_char *p, *last; + ssize_t n; + ngx_fd_t fd, fds; + struct tm tm; + ngx_str_t noname, *name; + ngx_uint_t i, frame_rate_num, frame_rate_denom; + ngx_uint_t depth_msec, depth_sec; + ngx_uint_t update_period, update_period_msec; + ngx_uint_t buffer_time, buffer_time_msec; + ngx_uint_t presentation_delay, presentation_delay_msec; + ngx_uint_t gcd, par_x, par_y; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_frag_t *f; + ngx_rtmp_dash_app_conf_t *dacf; + + ngx_rtmp_playlist_t v; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; + static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { + return NGX_ERROR; + } + + if (ctx->id == 0) { + ngx_rtmp_dash_write_init_segments(s); + } + + fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: open failed: '%V'", &ctx->playlist_bak); + return NGX_ERROR; + } + + /* write segments file */ + fds = ngx_open_file(ctx->segments_bak.data, NGX_FILE_WRONLY, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (fds == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: open failed: '%V'", &ctx->segments_bak); + return NGX_ERROR; + } + + /** + * Availability time must be equal stream start time + * Cos segments time counting from it + */ + ngx_libc_gmtime(ctx->start_time.sec, &tm); + *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", + tm.tm_year + 1900, tm.tm_mon + 1, + tm.tm_mday, tm.tm_hour, + tm.tm_min, tm.tm_sec + ) = 0; + + /* Stream publish time */ + *ngx_sprintf(publish_time, "%s", avaliable_time) = 0; + + depth_sec = (ngx_uint_t) ( + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration - + ngx_rtmp_dash_get_frag(s, 0)->timestamp); + + depth_msec = depth_sec % 1000; + depth_sec -= depth_msec; + depth_sec /= 1000; + + ngx_libc_gmtime(depth_sec, &tm); + + *ngx_sprintf(buffer_depth, "P%dY%02dM%02dDT%dH%02dM%02d.%03dS", + tm.tm_year - 70, tm.tm_mon, + tm.tm_mday - 1, tm.tm_hour, + tm.tm_min, tm.tm_sec, + depth_msec) = 0; + + last = buffer + sizeof(buffer); + + /** + * Calculate playlist minimal update period + * This should be more than biggest segment duration + * Cos segments rounded by keyframe/GOP. + * And that time not always equals to fragment length. + */ + update_period = dacf->fraglen; + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + if (f->duration > update_period) { + update_period = f->duration; + } + } + + // Reasonable delay for streaming + presentation_delay = update_period * 2 + 1000; + presentation_delay_msec = presentation_delay % 1000; + presentation_delay -= presentation_delay_msec; + presentation_delay /= 1000; + + // Calculate msec part and seconds + update_period_msec = update_period % 1000; + update_period -= update_period_msec; + update_period /= 1000; + + // Buffer length by default fragment length + buffer_time = dacf->fraglen; + buffer_time_msec = buffer_time % 1000; + buffer_time -= buffer_time_msec; + buffer_time /= 1000; + + // Fill DASH header + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, + // availabilityStartTime + avaliable_time, + // publishTime + publish_time, + // minimumUpdatePeriod + update_period, update_period_msec, + // minBufferTime + buffer_time, buffer_time_msec, + // timeShiftBufferDepth + buffer_depth, + // suggestedPresentationDelay + presentation_delay, presentation_delay_msec + ); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_PERIOD); + + n = ngx_write_fd(fd, buffer, p - buffer); + + ngx_str_null(&noname); + + name = (dacf->nested ? &noname : &ctx->name); + sep = (dacf->nested ? "" : "-"); + + if (ctx->has_video) { + frame_rate_num = (ngx_uint_t) (codec_ctx->frame_rate * 1000.); + + if (frame_rate_num % 1000 == 0) { + *ngx_sprintf(frame_rate, "%ui", frame_rate_num / 1000) = 0; + } else { + frame_rate_denom = 1000; + switch (frame_rate_num) { + case 23976: + frame_rate_num = 24000; + frame_rate_denom = 1001; + break; + case 29970: + frame_rate_num = 30000; + frame_rate_denom = 1001; + break; + case 59940: + frame_rate_num = 60000; + frame_rate_denom = 1001; + break; + } + + *ngx_sprintf(frame_rate, "%ui/%ui", frame_rate_num, frame_rate_denom) = 0; + } + + gcd = ngx_rtmp_dash_gcd(codec_ctx->width, codec_ctx->height); + par_x = codec_ctx->width / gcd; + par_y = codec_ctx->height / gcd; + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO, + codec_ctx->width, + codec_ctx->height, + frame_rate, + par_x, par_y); + + switch (dacf->ad_markers) { + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: + p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); + } + + n = ngx_write_fd(fd, buffer, p - buffer); + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, + &ctx->name, + codec_ctx->avc_profile, + codec_ctx->avc_compat, + codec_ctx->avc_level, + codec_ctx->width, + codec_ctx->height, + frame_rate, + (ngx_uint_t) (codec_ctx->video_data_rate * 1000), + name, sep, + name, sep); + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + f->timestamp, f->duration); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); + + ngx_write_fd(fds, buffer, p - buffer); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); + + n = ngx_write_fd(fd, buffer, p - buffer); + } + + if (ctx->has_audio) { + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, + &ctx->name, + codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? + (codec_ctx->aac_sbr ? "40.5" : "40.2") : "6b", + codec_ctx->sample_rate, + (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), + name, sep, + name, sep); + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + f->timestamp, f->duration); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER); + + n = ngx_write_fd(fd, buffer, p - buffer); + } + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_PERIOD_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); + + /* UTCTiming value */ + switch (dacf->clock_compensation) { + case NGX_RTMP_DASH_CLOCK_COMPENSATION_NTP: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "ntp", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_HEAD: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "http-head", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + case NGX_RTMP_DASH_CLOCK_COMPENSATION_HTTP_ISO: + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_CLOCK, + "http-iso", + &dacf->clock_helper_uri + ); + n = ngx_write_fd(fd, buffer, p - buffer); + break; + } + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); + + if (n < 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: write failed: '%V'", &ctx->playlist_bak); + ngx_close_file(fd); + return NGX_ERROR; + } + + ngx_close_file(fd); + ngx_close_file(fds); + + if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%V'->'%V'", + &ctx->playlist_bak, &ctx->playlist); + return NGX_ERROR; + } + + if (ngx_rtmp_dash_rename_file(ctx->segments_bak.data, ctx->segments.data) + == NGX_FILE_ERROR) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%V'->'%V'", + &ctx->segments_bak, &ctx->segments); + return NGX_ERROR; + } + + /* try to write the variant file only once, check the max flag */ + if (ctx->var && ctx->var->args.nelts > 3) { + return ngx_rtmp_dash_write_variant_playlist(s); + } + + ngx_memzero(&v, sizeof(v)); + ngx_str_set(&(v.module), "dash"); + v.playlist.data = ctx->playlist.data; + v.playlist.len = ctx->playlist.len; + return next_playlist(s, &v); +} + + +static ngx_int_t +ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) +{ + ngx_fd_t fd; + ngx_int_t rc; + ngx_buf_t b; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (ctx == NULL || codec_ctx == NULL) { + return NGX_ERROR; + } + + /* init video */ + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4v") = 0; + + fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating video init file"); + return NGX_ERROR; + } + + b.start = buffer; + b.end = b.start + sizeof(buffer); + b.pos = b.last = b.start; + + ngx_rtmp_mp4_write_ftyp(&b); + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK); + + rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: writing video init failed"); + } + + ngx_close_file(fd); + + /* init audio */ + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4a") = 0; + + fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating dash audio init file"); + return NGX_ERROR; + } + + b.pos = b.last = b.start; + + ngx_rtmp_mp4_write_ftyp(&b); + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK); + + rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: writing audio init failed"); + } + + ngx_close_file(fd); + + return NGX_OK; +} + + +static void +ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) +{ + u_char *pos, *pos1; + size_t left; + ssize_t n; + ngx_fd_t fd; + ngx_buf_t b; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_frag_t *f; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + + if (!t->opened) { + return; + } + + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: close fragment id=%ui, type=%c, pts=%uD", + t->id, t->type, t->earliest_pres_time); + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + b.start = buffer; + b.end = buffer + sizeof(buffer); + b.pos = b.last = b.start; + + if (ctx->has_cuepoint) { + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint write emsg : epts='%ui', lpts='%ui', cpts='%ui', duration='%ui'", + t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_time, ctx->cuepoint_duration); + + /* ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, */ + ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ + ctx->cuepoint_time, + ctx->cuepoint_duration, + ctx->cuepoint_id); + + pos = b.last; + b.last = pos; + ctx->has_cuepoint = 0; + } + + ngx_rtmp_mp4_write_styp(&b); + + pos = b.last; + b.last += 44; /* leave room for sidx */ + + ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, + t->samples, t->sample_mask, t->id); + pos1 = b.last; + b.last = pos; + + ngx_rtmp_mp4_write_sidx(&b, t->mdat_size + 8 + (pos1 - (pos + 44)), + t->earliest_pres_time, t->latest_pres_time); + b.last = pos1; + ngx_rtmp_mp4_write_mdat(&b, t->mdat_size + 8); + + /* move the data down to make room for the headers */ + + f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uD.m4%c", + f->timestamp, t->type) = 0; + + fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating dash temp video file"); + goto done; + } + + if (ngx_write_fd(fd, b.pos, (size_t) (b.last - b.pos)) == NGX_ERROR) { + goto done; + } + + left = (size_t) t->mdat_size; + +#if (NGX_WIN32) + if (SetFilePointer(t->fd, 0, 0, FILE_BEGIN) == INVALID_SET_FILE_POINTER) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: SetFilePointer error"); + goto done; + } +#else + if (lseek(t->fd, 0, SEEK_SET) == -1) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: lseek error"); + goto done; + } +#endif + + while (left > 0) { + + n = ngx_read_fd(t->fd, buffer, ngx_min(sizeof(buffer), left)); + if (n == NGX_ERROR) { + break; + } + + n = ngx_write_fd(fd, buffer, (size_t) n); + if (n == NGX_ERROR) { + break; + } + + left -= n; + } + +done: + + if (fd != NGX_INVALID_FILE) { + ngx_close_file(fd); + } + + ngx_close_file(t->fd); + + t->fd = NGX_INVALID_FILE; + t->opened = 0; +} + + +static ngx_int_t +ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) +{ + ngx_rtmp_dash_ctx_t *ctx; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + if (ctx == NULL || !ctx->opened) { + return NGX_OK; + } + + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: close fragments"); + + ngx_rtmp_dash_close_fragment(s, &ctx->video); + ngx_rtmp_dash_close_fragment(s, &ctx->audio); + + ngx_rtmp_dash_next_frag(s); + + ngx_rtmp_dash_write_playlist(s); + + ctx->id++; + ctx->opened = 0; + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, + ngx_uint_t id, char type) +{ + ngx_rtmp_dash_ctx_t *ctx; + + if (t->opened) { + return NGX_OK; + } + + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: open fragment id=%ui, type='%c'", id, type); + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "raw.m4%c", type) = 0; + + t->fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (t->fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating fragment file"); + return NGX_ERROR; + } + + t->id = id; + t->type = type; + t->sample_count = 0; + t->earliest_pres_time = 0; + t->latest_pres_time = 0; + t->mdat_size = 0; + t->opened = 1; + + if (type == 'v') { + t->sample_mask = NGX_RTMP_MP4_SAMPLE_SIZE| + NGX_RTMP_MP4_SAMPLE_DURATION| + NGX_RTMP_MP4_SAMPLE_DELAY| + NGX_RTMP_MP4_SAMPLE_KEY; + } else { + t->sample_mask = NGX_RTMP_MP4_SAMPLE_SIZE| + NGX_RTMP_MP4_SAMPLE_DURATION; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) +{ + ngx_rtmp_dash_ctx_t *ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: open fragments"); + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + if (ctx->opened) { + return NGX_OK; + } + + if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) { + return NGX_ERROR; + } + + ngx_rtmp_dash_open_fragment(s, &ctx->video, ctx->id, 'v'); + + ngx_rtmp_dash_open_fragment(s, &ctx->audio, ctx->id, 'a'); + + ctx->opened = 1; + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s) +{ + size_t len; + ngx_file_info_t fi; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; + + static u_char path[NGX_MAX_PATH + 1]; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + + *ngx_snprintf(path, sizeof(path) - 1, "%V", &dacf->path) = 0; + + if (ngx_file_info(path, &fi) == NGX_FILE_ERROR) { + + if (ngx_errno != NGX_ENOENT) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: " ngx_file_info_n " failed on '%V'", + &dacf->path); + return NGX_ERROR; + } + + /* ENOENT */ + + if (ngx_create_dir(path, NGX_RTMP_DASH_DIR_ACCESS) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: " ngx_create_dir_n " failed on '%V'", + &dacf->path); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: directory '%V' created", &dacf->path); + + } else { + + if (!ngx_is_dir(&fi)) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: '%V' exists and is not a directory", + &dacf->path); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: directory '%V' exists", &dacf->path); + } + + if (!dacf->nested) { + return NGX_OK; + } + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + len = dacf->path.len; + if (dacf->path.data[len - 1] == '/') { + len--; + } + + *ngx_snprintf(path, sizeof(path) - 1, "%*s/%V", len, dacf->path.data, + &ctx->name) = 0; + + if (ngx_file_info(path, &fi) != NGX_FILE_ERROR) { + + if (ngx_is_dir(&fi)) { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: directory '%s' exists", path); + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: '%s' exists and is not a directory", path); + + return NGX_ERROR; + } + + if (ngx_errno != NGX_ENOENT) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: " ngx_file_info_n " failed on '%s'", path); + return NGX_ERROR; + } + + /* NGX_ENOENT */ + + if (ngx_create_dir(path, NGX_RTMP_DASH_DIR_ACCESS) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: " ngx_create_dir_n " failed on '%s'", path); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: directory '%s' created", path); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) +{ + u_char *p, *pp; + size_t len; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_frag_t *f; + ngx_rtmp_dash_app_conf_t *dacf; + ngx_rtmp_dash_variant_t *var; + ngx_uint_t n; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { + goto next; + } + + if (s->auto_pushed) { + goto next; + } + + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: publish: name='%s' type='%s'", v->name, v->type); + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + if (ctx == NULL) { + ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_rtmp_dash_ctx_t)); + if (ctx == NULL) { + goto next; + } + ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_dash_module); + + } else { + if (ctx->opened) { + goto next; + } + + f = ctx->frags; + ngx_memzero(ctx, sizeof(ngx_rtmp_dash_ctx_t)); + ctx->frags = f; + } + + if (ctx->frags == NULL) { + ctx->frags = ngx_pcalloc(s->connection->pool, + sizeof(ngx_rtmp_dash_frag_t) * + (dacf->winfrags * 2 + 1)); + if (ctx->frags == NULL) { + return NGX_ERROR; + } + } + + ctx->id = 0; + + if (ngx_strstr(v->name, "..")) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: bad stream name: '%s'", v->name); + return NGX_ERROR; + } + + ctx->name.len = ngx_strlen(v->name); + ctx->name.data = ngx_palloc(s->connection->pool, ctx->name.len + 1); + + if (ctx->name.data == NULL) { + return NGX_ERROR; + } + + *ngx_cpymem(ctx->name.data, v->name, ctx->name.len) = 0; + + len = dacf->path.len + 1 + ctx->name.len + sizeof(".mpd"); + if (dacf->nested) { + len += sizeof("/index") - 1; + } + + ctx->playlist.data = ngx_palloc(s->connection->pool, len); + p = ngx_cpymem(ctx->playlist.data, dacf->path.data, dacf->path.len); + + if (p[-1] != '/') { + *p++ = '/'; + } + + p = ngx_cpymem(p, ctx->name.data, ctx->name.len); + + /* + * ctx->stream holds initial part of stream file path + * however the space for the whole stream path + * is allocated + */ + + ctx->stream.len = p - ctx->playlist.data + 1; + ctx->stream.data = ngx_palloc(s->connection->pool, + ctx->stream.len + NGX_INT32_LEN + + sizeof(".m4x")); + + ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1); + ctx->stream.data[ctx->stream.len - 1] = (dacf->nested ? '/' : '-'); + + if (dacf->variant) { + var = dacf->variant->elts; + for (n = 0; n < dacf->variant->nelts; n++, var++) { + if (ctx->name.len > var->suffix.len && + ngx_memcmp(var->suffix.data, + ctx->name.data + ctx->name.len - var->suffix.len, + var->suffix.len) + == 0) + { + len = (size_t) (ctx->name.len - var->suffix.len); + + ctx->varname.len = len; + ctx->varname.data = ngx_palloc(s->connection->pool, + ctx->varname.len + 1); + pp = ngx_cpymem(ctx->varname.data, + ctx->name.data, len); + + *pp = 0; + + ctx->var = var; + + len = (size_t) (p - ctx->playlist.data); + + ctx->var_playlist.len = len - var->suffix.len + sizeof(".mpd") + -1; + ctx->var_playlist.data = ngx_palloc(s->connection->pool, + ctx->var_playlist.len + 1); + pp = ngx_cpymem(ctx->var_playlist.data, + ctx->playlist.data, len - var->suffix.len); + pp = ngx_cpymem(pp, ".mpd", sizeof(".mpd") - 1); + *pp = 0; + + ctx->var_playlist_bak.len = ctx->var_playlist.len + + sizeof(".bak") - 1; + ctx->var_playlist_bak.data = ngx_palloc(s->connection->pool, + ctx->var_playlist_bak.len + 1); + pp = ngx_cpymem(ctx->var_playlist_bak.data, + ctx->var_playlist.data, + ctx->var_playlist.len); + pp = ngx_cpymem(pp, ".bak", sizeof(".bak") - 1); + *pp = 0; + + break; + } + } + } + + if (dacf->nested) { + p = ngx_cpymem(p, "/index.mpd", sizeof("/index.mpd") - 1); + } else { + p = ngx_cpymem(p, ".mpd", sizeof(".mpd") - 1); + } + + ctx->playlist.len = p - ctx->playlist.data; + + *p = 0; + + /* playlist bak (new playlist) path */ + + ctx->playlist_bak.data = ngx_palloc(s->connection->pool, + ctx->playlist.len + sizeof(".bak")); + p = ngx_cpymem(ctx->playlist_bak.data, ctx->playlist.data, + ctx->playlist.len); + p = ngx_cpymem(p, ".bak", sizeof(".bak") - 1); + + ctx->playlist_bak.len = p - ctx->playlist_bak.data; + + *p = 0; + + /* segments path */ + + ctx->segments.data = ngx_palloc(s->connection->pool, + ctx->playlist.len - 4 + sizeof(".seg")); + p = ngx_cpymem(ctx->segments.data, ctx->playlist.data, + ctx->playlist.len - 4); + p = ngx_cpymem(p, ".seg", sizeof(".seg") - 1); + + ctx->segments.len = p - ctx->segments.data; + + *p = 0; + + /* segments bak (new segments) path */ + + ctx->segments_bak.data = ngx_palloc(s->connection->pool, + ctx->playlist.len - 4 + sizeof(".sbk")); + p = ngx_cpymem(ctx->segments_bak.data, ctx->playlist.data, + ctx->playlist.len - 4); + p = ngx_cpymem(p, ".sbk", sizeof(".sbk") - 1); + + ctx->segments_bak.len = p - ctx->segments_bak.data; + + *p = 0; + + ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: playlist='%V' playlist_bak='%V' segments='%V' segments_bak='%V' stream_pattern='%V'", + &ctx->playlist, &ctx->playlist_bak, &ctx->segments, &ctx->segments_bak, &ctx->stream); + + ctx->start_time = *ngx_cached_time; + + if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) { + return NGX_ERROR; + } + +next: + return next_publish(s, v); +} + + +static ngx_int_t +ngx_rtmp_dash_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + if (dacf == NULL || !dacf->dash || ctx == NULL) { + goto next; + } + + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: delete stream"); + + ngx_rtmp_dash_close_fragments(s); + +next: + return next_close_stream(s, v); +} + +static void +ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, + uint32_t timestamp) +{ + int32_t d; + ngx_int_t hit; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_frag_t *f; + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + + ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: timestamp=%ui, f-timestamp=%ui, boundary=%i, dacf-fraglen=%ui", + timestamp, f->timestamp, boundary, dacf->fraglen); + + d = (int32_t) (timestamp - f->timestamp); + + if (d >= 0) { + + f->duration = timestamp - f->timestamp; + hit = (f->duration >= dacf->fraglen); + + } else { + + /* sometimes clients generate slightly unordered frames */ + + hit = (-d > 1000); + } + + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: d=%i, f-duration=%ui, hit=%i", + d, f->duration, hit); + + if (ctx->has_video && !hit) { + boundary = 0; + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: boundary=0 cos has_video && !hit"); + } + + if (!ctx->has_video && ctx->has_audio) { + boundary = hit; + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: boundary=hit cos !has_video && has_audio"); + } + + if (ctx->audio.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) { + boundary = 1; + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: boundary=1 cos audio max mdat"); + } + + if (ctx->video.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) { + boundary = 1; + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: boundary=1 cos video max mdat"); + } + + if (!ctx->opened) { + boundary = 1; + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: boundary=1 cos !opened"); + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: update_fragments: boundary=%i", + boundary); + + if (boundary) { + ngx_rtmp_dash_close_fragments(s); + ngx_rtmp_dash_open_fragments(s); + + f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + f->timestamp = timestamp; + } +} + + +static ngx_int_t +ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, + ngx_rtmp_dash_track_t *t, ngx_int_t key, uint32_t timestamp, uint32_t delay) +{ + u_char *p; + size_t size, bsize; + ngx_rtmp_mp4_sample_t *smpl; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + + p = buffer; + size = 0; + + for (; in && size < sizeof(buffer); in = in->next) { + + bsize = (size_t) (in->buf->last - in->buf->pos); + if (size + bsize > sizeof(buffer)) { + bsize = (size_t) (sizeof(buffer) - size); + } + + p = ngx_cpymem(p, in->buf->pos, bsize); + size += bsize; + } + + ngx_rtmp_dash_update_fragments(s, key, timestamp); + + if (t->sample_count == 0) { + t->earliest_pres_time = timestamp; + } + + t->latest_pres_time = timestamp; + + if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { + + if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: " ngx_write_fd_n " failed"); + return NGX_ERROR; + } + + smpl = &t->samples[t->sample_count]; + + smpl->delay = delay; + smpl->size = (uint32_t) size; + smpl->duration = 0; + smpl->timestamp = timestamp; + smpl->key = (key ? 1 : 0); + + if (t->sample_count > 0) { + smpl = &t->samples[t->sample_count - 1]; + smpl->duration = timestamp - smpl->timestamp; + } + + t->sample_count++; + t->mdat_size += (ngx_uint_t) size; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + u_char htype; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (dacf == NULL || !dacf->dash || ctx == NULL || + codec_ctx == NULL || h->mlen < 2) + { + return NGX_OK; + } + + /* Only AAC is supported */ + + if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC || + codec_ctx->aac_header == NULL) + { + return NGX_OK; + } + + if (in->buf->last - in->buf->pos < 2) { + return NGX_ERROR; + } + + /* skip AAC config */ + + htype = in->buf->pos[1]; + if (htype != 1) { + return NGX_OK; + } + + ctx->has_audio = 1; + + /* skip RTMP & AAC headers */ + + in->buf->pos += 2; + + return ngx_rtmp_dash_append(s, in, &ctx->audio, 0, h->timestamp, 0); +} + + +static ngx_int_t +ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + u_char *p; + uint8_t ftype, htype; + uint32_t delay; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (dacf == NULL || !dacf->dash || ctx == NULL || codec_ctx == NULL || + codec_ctx->avc_header == NULL || h->mlen < 5) + { + return NGX_OK; + } + + /* Only H264 is supported */ + + if (codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) { + return NGX_OK; + } + + if (in->buf->last - in->buf->pos < 5) { + return NGX_ERROR; + } + + ftype = (in->buf->pos[0] & 0xf0) >> 4; + + /* skip AVC config */ + + htype = in->buf->pos[1]; + if (htype != 1) { + return NGX_OK; + } + + p = (u_char *) &delay; + + p[0] = in->buf->pos[4]; + p[1] = in->buf->pos[3]; + p[2] = in->buf->pos[2]; + p[3] = 0; + + ctx->has_video = 1; + + /* skip RTMP & H264 headers */ + + in->buf->pos += 5; + + return ngx_rtmp_dash_append(s, in, &ctx->video, ftype == 1, h->timestamp, + delay); +} + + +static ngx_int_t +ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) +{ + return next_stream_begin(s, v); +} + + +static ngx_int_t +ngx_rtmp_dash_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v) +{ + ngx_rtmp_dash_close_fragments(s); + + return next_stream_eof(s, v); +} + + +static ngx_int_t +ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) +{ + time_t mtime, max_age; + u_char *p; + u_char path[NGX_MAX_PATH + 1], mpd_path[NGX_MAX_PATH + 1]; + ngx_dir_t dir; + ngx_err_t err; + ngx_str_t name, spath, mpd; + ngx_int_t nentries, nerased; + ngx_file_info_t fi; + + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, + "dash: cleanup path='%V' playlen=%M", ppath, playlen); + + if (ngx_open_dir(ppath, &dir) != NGX_OK) { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, ngx_errno, + "dash: cleanup open dir failed '%V'", ppath); + return NGX_ERROR; + } + + nentries = 0; + nerased = 0; + + for ( ;; ) { + ngx_set_errno(0); + + if (ngx_read_dir(&dir) == NGX_ERROR) { + err = ngx_errno; + + if (ngx_close_dir(&dir) == NGX_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, + "dash: cleanup " ngx_close_dir_n " \"%V\" failed", + ppath); + } + + if (err == NGX_ENOMOREFILES) { + return nentries - nerased; + } + + ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, err, + "dash: cleanup " ngx_read_dir_n + " '%V' failed", ppath); + return NGX_ERROR; + } + + name.data = ngx_de_name(&dir); + if (name.data[0] == '.') { + continue; + } + + name.len = ngx_de_namelen(&dir); + + p = ngx_snprintf(path, sizeof(path) - 1, "%V/%V", ppath, &name); + *p = 0; + + spath.data = path; + spath.len = p - path; + + nentries++; + + if (!dir.valid_info && ngx_de_info(path, &dir) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, + "dash: cleanup " ngx_de_info_n " \"%V\" failed", + &spath); + + continue; + } + + if (ngx_de_is_dir(&dir)) { + + if (ngx_rtmp_dash_cleanup_dir(&spath, playlen) == 0) { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, + "dash: cleanup dir '%V'", &name); + + /* + * null-termination gets spoiled in win32 + * version of ngx_open_dir + */ + + *p = 0; + + if (ngx_delete_dir(path) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "dash: cleanup " ngx_delete_dir_n + " failed on '%V'", &spath); + } else { + nerased++; + } + } + + continue; + } + + if (!ngx_de_is_file(&dir)) { + continue; + } + + if (name.len >= 8 && name.data[name.len - 8] == 'i' && + name.data[name.len - 7] == 'n' && + name.data[name.len - 6] == 'i' && + name.data[name.len - 5] == 't' && + name.data[name.len - 4] == '.' && + name.data[name.len - 3] == 'm' && + name.data[name.len - 2] == '4') + { + if (name.len == 8) { + ngx_str_set(&mpd, "index"); + } else { + mpd.data = name.data; + mpd.len = name.len - 9; + } + + p = ngx_snprintf(mpd_path, sizeof(mpd_path) - 1, "%V/%V.mpd", + ppath, &mpd); + *p = 0; + + if (ngx_file_info(mpd_path, &fi) != NGX_FILE_ERROR) { + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, + "dash: cleanup '%V' delayed, mpd exists '%s'", + &name, mpd_path); + continue; + } + + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, + "dash: cleanup '%V' allowed, mpd missing '%s'", + &name, mpd_path); + + max_age = 0; + + } else if (name.len >= 4 && name.data[name.len - 4] == '.' && + name.data[name.len - 3] == 'm' && + name.data[name.len - 2] == '4' && + name.data[name.len - 1] == 'v') + { + max_age = playlen / 500; + + } else if (name.len >= 4 && name.data[name.len - 4] == '.' && + name.data[name.len - 3] == 'm' && + name.data[name.len - 2] == '4' && + name.data[name.len - 1] == 'a') + { + max_age = playlen / 500; + + } else if (name.len >= 4 && name.data[name.len - 4] == '.' && + name.data[name.len - 3] == 'm' && + name.data[name.len - 2] == 'p' && + name.data[name.len - 1] == 'd') + { + max_age = playlen / 500; + + } else if (name.len >= 4 && name.data[name.len - 4] == '.' && + name.data[name.len - 3] == 'r' && + name.data[name.len - 2] == 'a' && + name.data[name.len - 1] == 'w') + { + max_age = playlen / 500; + + } else { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, + "dash: cleanup skip unknown file type '%V'", &name); + continue; + } + + mtime = ngx_de_mtime(&dir); + if (mtime + max_age > ngx_cached_time->sec) { + continue; + } + + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, + "dash: cleanup '%V' mtime=%T age=%T", + &name, mtime, ngx_cached_time->sec - mtime); + + if (ngx_delete_file(path) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "dash: cleanup " ngx_delete_file_n " failed on '%V'", + &spath); + continue; + } + + nerased++; + } +} + + +#if (nginx_version >= 1011005) +static ngx_msec_t +#else +static time_t +#endif +ngx_rtmp_dash_cleanup(void *data) +{ + ngx_rtmp_dash_cleanup_t *cleanup = data; + + ngx_rtmp_dash_cleanup_dir(&cleanup->path, cleanup->playlen); + + // Next callback in doubled playlist length time to make sure what all + // players read all segments +#if (nginx_version >= 1011005) + return cleanup->playlen * 2; +#else + return cleanup->playlen / 500; +#endif +} + + +static char * +ngx_rtmp_dash_variant(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_rtmp_dash_app_conf_t *dacf = conf; + + ngx_str_t *value, *arg; + ngx_uint_t n; + ngx_rtmp_dash_variant_t *var; + + value = cf->args->elts; + + if (dacf->variant == NULL) { + dacf->variant = ngx_array_create(cf->pool, 1, + sizeof(ngx_rtmp_dash_variant_t)); + if (dacf->variant == NULL) { + return NGX_CONF_ERROR; + } + } + + var = ngx_array_push(dacf->variant); + if (var == NULL) { + return NGX_CONF_ERROR; + } + + ngx_memzero(var, sizeof(ngx_rtmp_dash_variant_t)); + + var->suffix = value[1]; + + if (cf->args->nelts == 2) { + return NGX_CONF_OK; + } + + if (ngx_array_init(&var->args, cf->pool, cf->args->nelts - 2, + sizeof(ngx_str_t)) + != NGX_OK) + { + return NGX_CONF_ERROR; + } + + arg = ngx_array_push_n(&var->args, cf->args->nelts - 2); + if (arg == NULL) { + return NGX_CONF_ERROR; + } + + for (n = 2; n < cf->args->nelts; n++) { + *arg++ = value[n]; + } + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v) +{ + return next_playlist(s, v); +} + + +static ngx_int_t +ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_int_t res; + ngx_rtmp_dash_ctx_t *ctx; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + static struct { + double time; + double duration; + u_char name[128]; + u_char type[128]; + u_char ptype[128]; + } v; + + static ngx_rtmp_amf_elt_t in_pr_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.ptype, sizeof(v.ptype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("duration"), + &v.duration, sizeof(v.duration) }, + + }; + + static ngx_rtmp_amf_elt_t in_dt_elts[] = { + + { NGX_RTMP_AMF_NUMBER, + ngx_string("time"), + &v.time, sizeof(v.time) }, + + { NGX_RTMP_AMF_STRING, + ngx_string("name"), + v.name, sizeof(v.name) }, + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.type, sizeof(v.type) }, + + { NGX_RTMP_AMF_OBJECT, + ngx_string("parameters"), + in_pr_elts, sizeof(in_pr_elts) }, + + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + in_dt_elts, sizeof(in_dt_elts) }, + + }; + + ngx_memzero(&v, sizeof(v)); + res = ngx_rtmp_receive_amf(s, in, in_elts, + sizeof(in_elts) / sizeof(in_elts[0])); + + if (res == NGX_OK) { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'", + h->timestamp, v.time, v.name, v.type, v.ptype, v.duration); + } else { + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash : onCuepoint : amf not understood"); + } + + ctx->has_cuepoint = 1; + ctx->cuepoint_time = h->timestamp; + ctx->cuepoint_duration = v.duration; + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_int_t res; + ngx_rtmp_dash_ctx_t *ctx; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + static struct { + unsigned ooni; + unsigned splice_event_ci; + unsigned splice_imd; + double avail_num; + double avail_expected; + double duration; + double prtime; + double prgid; + double sctype; + double sevtid; + u_char type[128]; + u_char mtype[128]; + } v; + + static ngx_rtmp_amf_elt_t in_pr_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("messageType"), + v.mtype, sizeof(v.mtype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("splice_command_type"), + &v.sctype, sizeof(v.sctype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("splice_event_id"), + &v.sevtid, sizeof(v.sevtid) }, + + { NGX_RTMP_AMF_BOOLEAN, + ngx_string("splice_event_cancel_indicator"), + &v.splice_event_ci, sizeof(v.splice_event_ci) }, + + { NGX_RTMP_AMF_BOOLEAN, + ngx_string("out_of_network_indicator"), + &v.ooni, sizeof(v.ooni) }, + + { NGX_RTMP_AMF_BOOLEAN, + ngx_string("splice_immediate"), + &v.splice_imd, sizeof(v.splice_imd) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("pre_roll_time"), + &v.prtime, sizeof(v.prtime) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("break_duration"), + &v.duration, sizeof(v.duration) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("unique_program_id"), + &v.prgid, sizeof(v.prgid) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("avail_num"), + &v.avail_num, sizeof(v.avail_num) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("avail_expected"), + &v.avail_expected, sizeof(v.avail_expected) }, + + }; + + static ngx_rtmp_amf_elt_t in_dt_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.type, sizeof(v.type) }, + + { NGX_RTMP_AMF_OBJECT, + ngx_string("parameters"), + in_pr_elts, sizeof(in_pr_elts) }, + + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + in_dt_elts, sizeof(in_dt_elts) }, + + }; + + ngx_memzero(&v, sizeof(v)); + res = ngx_rtmp_receive_amf(s, in, in_elts, + sizeof(in_elts) / sizeof(in_elts[0])); + + if (res == NGX_OK && v.duration > 0) { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint_scte35 : ts='%ui', type='%s', mtype='%s', sctype='%f', "\ + "scid='%f', prgid='%f', duration='%f', avail_num='%f', avail_expected='%f'", + h->timestamp, v.type, v.mtype, v.sctype, + v.sevtid, v.prgid, v.duration, v.avail_num, v.avail_expected); + } else { + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash : onCuepoint_scte35 : amf not understood"); + } + + if (v.duration > 0) { + ctx->has_cuepoint = 1; + ctx->cuepoint_time = h->timestamp; + ctx->cuepoint_duration = v.duration; + ctx->cuepoint_id = v.prgid; + } + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_ad_markers(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_rtmp_dash_app_conf_t *dacf; + + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { + return NGX_OK; + } + + switch (dacf->ad_markers) { + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: + return ngx_rtmp_dash_on_cuepoint(s, h, in); + break; + case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: + return ngx_rtmp_dash_on_cuepoint_scte35(s, h, in); + break; + default: + return NGX_OK; + } + + return NGX_OK; +} + + +static void * +ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) +{ + ngx_rtmp_dash_app_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_rtmp_dash_app_conf_t)); + if (conf == NULL) { + return NULL; + } + + conf->dash = NGX_CONF_UNSET; + conf->fraglen = NGX_CONF_UNSET_MSEC; + conf->playlen = NGX_CONF_UNSET_MSEC; + conf->cleanup = NGX_CONF_UNSET; + conf->nested = NGX_CONF_UNSET; + conf->clock_compensation = NGX_CONF_UNSET; + conf->ad_markers = NGX_CONF_UNSET; + + return conf; +} + + +static char * +ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_rtmp_dash_app_conf_t *prev = parent; + ngx_rtmp_dash_app_conf_t *conf = child; + ngx_rtmp_dash_cleanup_t *cleanup; + + ngx_conf_merge_value(conf->dash, prev->dash, 0); + ngx_conf_merge_msec_value(conf->fraglen, prev->fraglen, 5000); + ngx_conf_merge_msec_value(conf->playlen, prev->playlen, 30000); + ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1); + ngx_conf_merge_value(conf->nested, prev->nested, 0); + ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, + NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); + ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); + ngx_conf_merge_uint_value(conf->ad_markers, prev->ad_markers, + NGX_RTMP_DASH_AD_MARKERS_OFF); + + if (conf->fraglen) { + conf->winfrags = conf->playlen / conf->fraglen; + } + + /* schedule cleanup */ + + if (conf->dash && conf->path.len && conf->cleanup) { + if (conf->path.data[conf->path.len - 1] == '/') { + conf->path.len--; + } + + cleanup = ngx_pcalloc(cf->pool, sizeof(*cleanup)); + if (cleanup == NULL) { + return NGX_CONF_ERROR; + } + + cleanup->path = conf->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_dash_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, ""); + + return NGX_CONF_OK; +} + + +static ngx_int_t +ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) +{ + ngx_rtmp_handler_pt *h; + ngx_rtmp_core_main_conf_t *cmcf; + 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_VIDEO]); + *h = ngx_rtmp_dash_video; + + h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]); + *h = ngx_rtmp_dash_audio; + + next_publish = ngx_rtmp_publish; + ngx_rtmp_publish = ngx_rtmp_dash_publish; + + next_close_stream = ngx_rtmp_close_stream; + ngx_rtmp_close_stream = ngx_rtmp_dash_close_stream; + + next_stream_begin = ngx_rtmp_stream_begin; + ngx_rtmp_stream_begin = ngx_rtmp_dash_stream_begin; + + next_stream_eof = ngx_rtmp_stream_eof; + ngx_rtmp_stream_eof = ngx_rtmp_dash_stream_eof; + + next_playlist = ngx_rtmp_playlist; + ngx_rtmp_playlist = ngx_rtmp_dash_playlist; + + ch = ngx_array_push(&cmcf->amf); + ngx_str_set(&ch->name, "onCuePoint"); + ch->handler = ngx_rtmp_dash_ad_markers; + + return NGX_OK; +} From a2cb48407e5a0f253ff31c7cf8e935d0e22c5cb0 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 30 Jun 2017 16:27:55 +0000 Subject: [PATCH 037/113] Working version with segment file ? --- dash/ngx_rtmp_dash_module.c | 61 ++++++++++++++++++++++++++----------- 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e5cfd15b9..8b080917b 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -317,7 +317,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) char *sep; u_char *p, *last; ssize_t n; - ngx_fd_t fd; + ngx_fd_t fd, fds; struct tm tm; ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; @@ -339,6 +339,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; + static u_char seg_path[NGX_MAX_PATH + 1]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -486,9 +487,11 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } + n = ngx_write_fd(fd, buffer, p - buffer); + for (j = 0; j < dacf->variant->nelts; j++, var++) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, &ctx->varname, &var->suffix, codec_ctx->avc_profile, codec_ctx->avc_compat, @@ -499,23 +502,46 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); } - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO, &ctx->varname, &var->suffix, sep, &ctx->varname, &var->suffix, sep); - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); + n = ngx_write_fd(fd, buffer, p - buffer); + + /* read segments file */ + if (dacf->nested) { + *ngx_sprintf(seg_path, "%V/%V%V/index.seg", + &dacf->path, &ctx->varname, &var->suffix) = 0; + } else { + *ngx_sprintf(seg_path, "%V/%V%V.seg", + &dacf->path, &ctx->varname, &var->suffix) = 0; } - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); - } + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: read segments file for variant '%s'", seg_path); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); + fds = ngx_open_file(seg_path, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (fds == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: open failed: segments '%s'", seg_path); + continue; + } + + while ((n = ngx_read_fd(fds, buffer, sizeof(buffer)))) { + n = ngx_write_fd(fd, buffer, n); + } + ngx_close_file(fds); + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); + + } + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); } @@ -790,10 +816,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - - n = ngx_write_fd(fd, buffer, p - buffer); - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, codec_ctx->avc_profile, codec_ctx->avc_compat, @@ -805,16 +829,19 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) name, sep, name, sep); + n = ngx_write_fd(fd, buffer, p - buffer); + + p = buffer; for (i = 0; i < ctx->nfrags; i++) { f = ngx_rtmp_dash_get_frag(s, i); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, f->timestamp, f->duration); } - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); - ngx_write_fd(fds, buffer, p - buffer); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); @@ -1026,8 +1053,8 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) "dash : onCuepoint write emsg : epts='%ui', lpts='%ui', cpts='%ui', duration='%ui'", t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_time, ctx->cuepoint_duration); - /* ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, */ - ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ + /* ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, */ + ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ ctx->cuepoint_time, ctx->cuepoint_duration, ctx->cuepoint_id); From bec65e8c5709bcc7b7296a61dd40d6a230092d9e Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 30 Jun 2017 16:43:53 +0000 Subject: [PATCH 038/113] Working version :) --- dash/ngx_rtmp_dash_module.c | 2 +- ngx_rtmp_handler.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 8b080917b..7ca4e4f2d 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -498,7 +498,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) codec_ctx->avc_level); arg = var->args.elts; - for (k = 0; k < var->args.nelts && k < 2 ; k++, arg++) { + for (k = 0; k < var->args.nelts && k < 3 ; k++, arg++) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); } diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index ff150175d..9ec57c5ed 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -419,7 +419,7 @@ ngx_rtmp_recv(ngx_event_t *rev) * TODO : make this configurable */ if (timestamp >= s->peer_epoch) { st->dtime = timestamp - s->peer_epoch; - } else if ((timestamp + 2000) >= s->peer_epoch) { + } else if ((timestamp + 10000) >= s->peer_epoch) { st->dtime = 0; } else { st->dtime = timestamp; @@ -428,7 +428,7 @@ ngx_rtmp_recv(ngx_event_t *rev) /* fix elemental live server sending garbage ts */ if ((int)h->type == 18) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18 and zero lenght", (int)fmt); - } else { + } else { h->timestamp = timestamp; st->dtime = 0; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP absolute timestamp on fmt type 0 : %uD", h->timestamp); From f76af364cc71db83b6a5d5b736588310ec7fa9e0 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 10 Jul 2017 11:39:18 +0000 Subject: [PATCH 039/113] Revert this pseudo fix as Elemental Live is definitively out of sync --- ngx_rtmp_handler.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index 9ec57c5ed..852e3cff1 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -417,15 +417,17 @@ ngx_rtmp_recv(ngx_event_t *rev) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); /* another elemental fix relative ts to epoch * TODO : make this configurable */ + /* if (timestamp >= s->peer_epoch) { st->dtime = timestamp - s->peer_epoch; } else if ((timestamp + 10000) >= s->peer_epoch) { st->dtime = 0; } else { st->dtime = timestamp; - } + }*/ + st->dtime = timestamp; } else { - /* fix elemental live server sending garbage ts */ + /* fix elemental live server sending garbage ts ?! */ if ((int)h->type == 18) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18 and zero lenght", (int)fmt); } else { From 86171ee55a84d7ff6d3ace98880468d1636e92e0 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 16 Oct 2017 19:50:16 +0000 Subject: [PATCH 040/113] Prelimaniry stuff to handle end marker, fix non scte oncuepoint --- dash/ngx_rtmp_dash_module.c | 26 +++++++++++++++++--------- dash/ngx_rtmp_mp4.c | 11 +++++++++-- dash/ngx_rtmp_mp4.h | 2 +- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index c52fa93de..74252b92b 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -78,7 +78,8 @@ typedef struct { unsigned opened:1; unsigned has_video:1; unsigned has_audio:1; - unsigned has_cuepoint:1; + unsigned start_cuepoint:1; + unsigned end_cuepoint:1; uint32_t cuepoint_time; uint32_t cuepoint_duration; @@ -1047,7 +1048,7 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) b.end = buffer + sizeof(buffer); b.pos = b.last = b.start; - if (ctx->has_cuepoint) { + if (ctx->start_cuepoint) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint write emsg : epts='%ui', lpts='%ui', cpts='%ui', duration='%ui'", @@ -1057,13 +1058,17 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ ctx->cuepoint_time, ctx->cuepoint_duration, - ctx->cuepoint_id); + ctx->cuepoint_id, + 1); /* start marker is 1 */ pos = b.last; b.last = pos; - ctx->has_cuepoint = 0; + ctx->start_cuepoint = 0; + ctx->end_cuepoint = 1; } + //TODO Handle end marker + // ngx_rtmp_mp4_write_styp(&b); pos = b.last; @@ -2159,7 +2164,7 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, res = ngx_rtmp_receive_amf(s, in, in_elts, sizeof(in_elts) / sizeof(in_elts[0])); - if (res == NGX_OK) { + if (res == NGX_OK && v.duration > 0) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'", h->timestamp, v.time, v.name, v.type, v.ptype, v.duration); @@ -2168,9 +2173,12 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, "dash : onCuepoint : amf not understood"); } - ctx->has_cuepoint = 1; - ctx->cuepoint_time = h->timestamp; - ctx->cuepoint_duration = v.duration; + if (v.duration > 0) { + ctx->start_cuepoint = 1; + ctx->cuepoint_time = h->timestamp; + ctx->cuepoint_duration = v.duration; + ctx->cuepoint_id = 0; + } return NGX_OK; } @@ -2284,7 +2292,7 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } if (v.duration > 0) { - ctx->has_cuepoint = 1; + ctx->start_cuepoint = 1; ctx->cuepoint_time = h->timestamp; ctx->cuepoint_duration = v.duration; ctx->cuepoint_id = v.prgid; diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 34745e181..5722e7c3b 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1168,7 +1168,7 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id) + uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id, uint32_t start_end_switch) { u_char *pos; uint32_t delta_time; @@ -1206,8 +1206,15 @@ ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, \ " +#define SCTE_START_AD "" +#define SCTE_END_AD "" + /* data */ - ngx_rtmp_mp4_data(b, SCTE_EVENT, sizeof(SCTE_EVENT)); + if (start_end_switch) { + ngx_rtmp_mp4_data(b, SCTE_START_AD, sizeof(SCTE_START_AD)); + } else { + ngx_rtmp_mp4_data(b, SCTE_END_AD, sizeof(SCTE_END_AD)); + } ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index ab010213b..fa60d82ab 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -48,6 +48,6 @@ ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, uint32_t latest_pres_time); ngx_int_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id); + uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id, uint32_t start_end_switch); #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ From 937498115ab9e3ad1ce2dce561edbaa2a3310181 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 18 Oct 2017 14:49:25 +0000 Subject: [PATCH 041/113] Small fix on timestamp delta --- ngx_rtmp_handler.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index 852e3cff1..df025f1f8 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -407,6 +407,7 @@ ngx_rtmp_recv(ngx_event_t *rev) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP extended timestamp %uD", (uint32_t)timestamp); } + /* type 0,1,2 */ if (fmt <= 2) { /* The specification states that ext timestamp * is present in Type 3 chunks when the most recent Type 0, @@ -414,22 +415,21 @@ ngx_rtmp_recv(ngx_event_t *rev) * an extended timestamp field. */ st->ext = ext; if (fmt) { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); - /* another elemental fix relative ts to epoch - * TODO : make this configurable */ - /* - if (timestamp >= s->peer_epoch) { - st->dtime = timestamp - s->peer_epoch; - } else if ((timestamp + 10000) >= s->peer_epoch) { - st->dtime = 0; - } else { + /* type 1,2 => timestamp delta */ + /* quick hack longer ts */ + if (timestamp < 20000 ) { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); st->dtime = timestamp; - }*/ - st->dtime = timestamp; + } else { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP spurious timestamp delta on fmt type %d, ignore it", (int)fmt); + st->dtime = 0; + } } else { + /* type */ /* fix elemental live server sending garbage ts ?! */ if ((int)h->type == 18) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18 and zero lenght", (int)fmt); + st->dtime = 0; } else { h->timestamp = timestamp; st->dtime = 0; From 361e40af95a95b38c3acfe24bbc030cdc9cf2c1b Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 20 Oct 2017 12:42:28 +0000 Subject: [PATCH 042/113] Another try fixing timestamp mess --- dash/ngx_rtmp_dash_module.c | 40 ++++++++++++++++++++++++++----------- ngx_rtmp_handler.c | 12 +++-------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 74252b92b..7e1110e5f 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -323,7 +323,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; - ngx_uint_t buffer_time, buffer_time_msec; + ngx_uint_t start_time, buffer_time, buffer_time_msec; ngx_uint_t presentation_delay, presentation_delay_msec; ngx_uint_t gcd, par_x, par_y; ngx_rtmp_dash_ctx_t *ctx; @@ -336,7 +336,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ngx_rtmp_playlist_t v; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; @@ -359,19 +359,27 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } + /* availabity and publish time should be relative to peer epoch */ + start_time = ctx->start_time.sec - (s->peer_epoch/1000); + ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "Fixing start_time=%uD %uD epoch=%uD new_start_time=%uD", + (uint32_t)ctx->start_time.sec, (uint32_t)ctx->start_time.msec, + (uint32_t)s->peer_epoch, + (uint32_t)start_time); + /** * Availability time must be equal stream start time * Cos segments time counting from it */ - ngx_libc_gmtime(ctx->start_time.sec, &tm); - *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", + ngx_libc_gmtime(start_time, &tm); + *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ) = 0; /* Stream publish time */ - *ngx_sprintf(publish_time, "%s", avaliable_time) = 0; + *ngx_sprintf(publish_time, "%s", available_time) = 0; depth_sec = (ngx_uint_t) ( ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + @@ -427,7 +435,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) // Fill DASH header p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, // availabilityStartTime - avaliable_time, + available_time, // publishTime publish_time, // minimumUpdatePeriod @@ -640,7 +648,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_uint_t i, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; - ngx_uint_t buffer_time, buffer_time_msec; + ngx_uint_t start_time, buffer_time, buffer_time_msec; ngx_uint_t presentation_delay, presentation_delay_msec; ngx_uint_t gcd, par_x, par_y; ngx_rtmp_dash_ctx_t *ctx; @@ -651,7 +659,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_playlist_t v; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - static u_char avaliable_time[NGX_RTMP_DASH_GMT_LENGTH]; + static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; @@ -687,19 +695,27 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } + /* availabity and publish time should be relative to peer epoch */ + start_time = ctx->start_time.sec - (s->peer_epoch/1000); + ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "Fixing start_time=%uD %uD epoch=%uD new_start_time=%uD", + (uint32_t)ctx->start_time.sec, (uint32_t)ctx->start_time.msec, + (uint32_t)s->peer_epoch, + (uint32_t)start_time); + /** * Availability time must be equal stream start time * Cos segments time counting from it */ - ngx_libc_gmtime(ctx->start_time.sec, &tm); - *ngx_sprintf(avaliable_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", + ngx_libc_gmtime(start_time, &tm); + *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ) = 0; /* Stream publish time */ - *ngx_sprintf(publish_time, "%s", avaliable_time) = 0; + *ngx_sprintf(publish_time, "%s", available_time) = 0; depth_sec = (ngx_uint_t) ( ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + @@ -755,7 +771,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) // Fill DASH header p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, // availabilityStartTime - avaliable_time, + available_time, // publishTime publish_time, // minimumUpdatePeriod diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index df025f1f8..c6b43c8bc 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -416,19 +416,13 @@ ngx_rtmp_recv(ngx_event_t *rev) st->ext = ext; if (fmt) { /* type 1,2 => timestamp delta */ - /* quick hack longer ts */ - if (timestamp < 20000 ) { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); - st->dtime = timestamp; - } else { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP spurious timestamp delta on fmt type %d, ignore it", (int)fmt); - st->dtime = 0; - } + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP timestamp delta on fmt type %d", (int)fmt); + st->dtime = timestamp; } else { /* type */ /* fix elemental live server sending garbage ts ?! */ if ((int)h->type == 18) { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18 and zero lenght", (int)fmt); + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP FIX fmt type %d type 18", (int)fmt); st->dtime = 0; } else { h->timestamp = timestamp; From 94849521473947a5e8cc8287e194dc62cb1e926c Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 23 Oct 2017 16:58:22 +0000 Subject: [PATCH 043/113] First try handling ad continuation message --- dash/ngx_rtmp_dash_module.c | 108 +++++++++++++++++++++++++++++++----- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 7e1110e5f..d20da193b 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1067,8 +1067,8 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) if (ctx->start_cuepoint) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint write emsg : epts='%ui', lpts='%ui', cpts='%ui', duration='%ui'", - t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_time, ctx->cuepoint_duration); + "dash : onCuepoint write emsg : epts='%uD', lpts='%uD', cpts='%uD', duration='%uD', prid='%uD'", + t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_time, ctx->cuepoint_duration, ctx->cuepoint_id); /* ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, */ ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ @@ -1080,11 +1080,12 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) pos = b.last; b.last = pos; ctx->start_cuepoint = 0; + ctx->cuepoint_duration = 0; ctx->end_cuepoint = 1; } //TODO Handle end marker - // + ngx_rtmp_mp4_write_styp(&b); pos = b.last; @@ -2184,16 +2185,13 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'", h->timestamp, v.time, v.name, v.type, v.ptype, v.duration); - } else { - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash : onCuepoint : amf not understood"); - } - - if (v.duration > 0) { ctx->start_cuepoint = 1; ctx->cuepoint_time = h->timestamp; ctx->cuepoint_duration = v.duration; ctx->cuepoint_id = 0; + } else { + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash : onCuepoint : amf not understood"); } return NGX_OK; @@ -2302,16 +2300,90 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, "scid='%f', prgid='%f', duration='%f', avail_num='%f', avail_expected='%f'", h->timestamp, v.type, v.mtype, v.sctype, v.sevtid, v.prgid, v.duration, v.avail_num, v.avail_expected); + ctx->start_cuepoint = 1; + ctx->cuepoint_time = h->timestamp; + ctx->cuepoint_duration = v.duration; + ctx->cuepoint_id = v.prgid; } else { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash : onCuepoint_scte35 : amf not understood"); } + + return NGX_OK; +} - if (v.duration > 0) { - ctx->start_cuepoint = 1; - ctx->cuepoint_time = h->timestamp; - ctx->cuepoint_duration = v.duration; - ctx->cuepoint_id = v.prgid; + +static ngx_int_t +ngx_rtmp_dash_on_cuepoint_cont_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_int_t res; + ngx_rtmp_dash_ctx_t *ctx; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + static struct { + double segupid; + double segduration; + double segrduration; + u_char type[128]; + u_char mtype[128]; + } v; + + static ngx_rtmp_amf_elt_t in_pr_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("messageType"), + v.mtype, sizeof(v.mtype) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("segmentation_upid"), + &v.segupid, sizeof(v.segupid) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("segmentation_duration"), + &v.segduration, sizeof(v.segduration) }, + + { NGX_RTMP_AMF_NUMBER, + ngx_string("segmentation_remaining_duration"), + &v.segrduration, sizeof(v.segrduration) }, + + }; + + static ngx_rtmp_amf_elt_t in_dt_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("type"), + v.type, sizeof(v.type) }, + + { NGX_RTMP_AMF_OBJECT, + ngx_string("parameters"), + in_pr_elts, sizeof(in_pr_elts) }, + + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + in_dt_elts, sizeof(in_dt_elts) }, + + }; + + ngx_memzero(&v, sizeof(v)); + res = ngx_rtmp_receive_amf(s, in, in_elts, + sizeof(in_elts) / sizeof(in_elts[0])); + + if (res == NGX_OK && v.segrduration > 0) { + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint__cont_scte35 : ts='%ui', type='%s', "\ + "segupid='%f', segduration='%f', segrduration='%f'", + h->timestamp, v.mtype, v.segupid, v.segduration, v.segrduration); + if (v.segrduration < 10000) + ctx->end_cuepoint = 0; + } else { + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash : onCuepoint_cont_scte35 : amf not understood"); } return NGX_OK; @@ -2323,18 +2395,24 @@ ngx_rtmp_dash_ad_markers(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_rtmp_dash_app_conf_t *dacf; + ngx_rtmp_dash_ctx_t *ctx; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { return NGX_OK; } + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + switch (dacf->ad_markers) { case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT: return ngx_rtmp_dash_on_cuepoint(s, h, in); break; case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: - return ngx_rtmp_dash_on_cuepoint_scte35(s, h, in); + if (ctx->end_cuepoint) + return ngx_rtmp_dash_on_cuepoint_cont_scte35(s, h, in); + else + return ngx_rtmp_dash_on_cuepoint_scte35(s, h, in); break; default: return NGX_OK; From 59e9a20efb48b33512a2305937457e3b38774074 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 25 Oct 2017 20:32:57 +0000 Subject: [PATCH 044/113] End marker round two, and simplification --- dash/ngx_rtmp_dash_module.c | 56 ++++++++++++++++++++++++------------- dash/ngx_rtmp_mp4.c | 11 ++------ dash/ngx_rtmp_mp4.h | 2 +- 3 files changed, 40 insertions(+), 29 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index d20da193b..4cb0f26bd 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -81,7 +81,8 @@ typedef struct { unsigned start_cuepoint:1; unsigned end_cuepoint:1; - uint32_t cuepoint_time; + uint32_t cuepoint_starttime; + uint32_t cuepoint_endtime; uint32_t cuepoint_duration; uint32_t cuepoint_id; @@ -1067,24 +1068,44 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) if (ctx->start_cuepoint) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint write emsg : epts='%uD', lpts='%uD', cpts='%uD', duration='%uD', prid='%uD'", - t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_time, ctx->cuepoint_duration, ctx->cuepoint_id); - - /* ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, */ - ngx_rtmp_mp4_write_emsg(&b, 0, /* should be earliest presentation time but dashjs is buggy */ - ctx->cuepoint_time, + "dash : onCuepoint write start emsg : epts='%uD', lpts='%uD', cpts='%uD', "\ + "ecpts='%uD', duration='%uD', prid='%uD'", + t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_starttime, + ctx->cuepoint_endtime, ctx->cuepoint_duration, ctx->cuepoint_id); + + /* should be ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, + thanks to dashjs + */ + ngx_rtmp_mp4_write_emsg(&b, 0, + ctx->cuepoint_starttime, ctx->cuepoint_duration, - ctx->cuepoint_id, - 1); /* start marker is 1 */ + ctx->cuepoint_id); pos = b.last; b.last = pos; ctx->start_cuepoint = 0; ctx->cuepoint_duration = 0; ctx->end_cuepoint = 1; - } - //TODO Handle end marker + } else if (ctx->end_cuepoint && ctx->cuepoint_endtime > t->earliest_pres_time + && ctx->cuepoint_endtime < t->latest_pres_time) { + + ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, + "dash : onCuepoint write end emsg : epts='%uD', lpts='%uD', cpts='%uD', "\ + "ecpts='%uD', duration='%uD', prid='%uD'", + t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_starttime, + ctx->cuepoint_endtime, ctx->cuepoint_duration, ctx->cuepoint_id); + + /* end marker have duration set to zero and prid set to zero */ + ngx_rtmp_mp4_write_emsg(&b, 0, + ctx->cuepoint_endtime, + 0, + 0); + + pos = b.last; + b.last = pos; + ctx->end_cuepoint = 0; + } ngx_rtmp_mp4_write_styp(&b); @@ -2186,7 +2207,8 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, "dash : onCuepoint : ts='%ui', time='%f', name='%s' type='%s' ptype='%s' duration='%f'", h->timestamp, v.time, v.name, v.type, v.ptype, v.duration); ctx->start_cuepoint = 1; - ctx->cuepoint_time = h->timestamp; + ctx->cuepoint_starttime = h->timestamp; + ctx->cuepoint_endtime = h->timestamp+(v.duration*1000); ctx->cuepoint_duration = v.duration; ctx->cuepoint_id = 0; } else { @@ -2301,7 +2323,8 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, h->timestamp, v.type, v.mtype, v.sctype, v.sevtid, v.prgid, v.duration, v.avail_num, v.avail_expected); ctx->start_cuepoint = 1; - ctx->cuepoint_time = h->timestamp; + ctx->cuepoint_starttime = h->timestamp; + ctx->cuepoint_endtime = h->timestamp + v.duration; ctx->cuepoint_duration = v.duration; ctx->cuepoint_id = v.prgid; } else { @@ -2318,9 +2341,6 @@ ngx_rtmp_dash_on_cuepoint_cont_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t * ngx_chain_t *in) { ngx_int_t res; - ngx_rtmp_dash_ctx_t *ctx; - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); static struct { double segupid; @@ -2376,11 +2396,9 @@ ngx_rtmp_dash_on_cuepoint_cont_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t * if (res == NGX_OK && v.segrduration > 0) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, - "dash : onCuepoint__cont_scte35 : ts='%ui', type='%s', "\ + "dash : onCuepoint_cont_scte35 : ts='%ui', type='%s', "\ "segupid='%f', segduration='%f', segrduration='%f'", h->timestamp, v.mtype, v.segupid, v.segduration, v.segrduration); - if (v.segrduration < 10000) - ctx->end_cuepoint = 0; } else { ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash : onCuepoint_cont_scte35 : amf not understood"); diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 5722e7c3b..34745e181 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1168,7 +1168,7 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id, uint32_t start_end_switch) + uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id) { u_char *pos; uint32_t delta_time; @@ -1206,15 +1206,8 @@ ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, \ " -#define SCTE_START_AD "" -#define SCTE_END_AD "" - /* data */ - if (start_end_switch) { - ngx_rtmp_mp4_data(b, SCTE_START_AD, sizeof(SCTE_START_AD)); - } else { - ngx_rtmp_mp4_data(b, SCTE_END_AD, sizeof(SCTE_END_AD)); - } + ngx_rtmp_mp4_data(b, SCTE_EVENT, sizeof(SCTE_EVENT)); ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index fa60d82ab..ab010213b 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -48,6 +48,6 @@ ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, uint32_t latest_pres_time); ngx_int_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); ngx_int_t ngx_rtmp_mp4_write_emsg(ngx_buf_t *b, - uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id, uint32_t start_end_switch); + uint32_t earliest_pres_time, uint32_t cuepoint_time, uint32_t duration_time, uint32_t id); #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ From cf617afb91be4bedc5826c1eca7d38be30f0e4eb Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 25 Apr 2018 19:59:14 +0000 Subject: [PATCH 045/113] Add template for segment timeline with repetition --- dash/ngx_rtmp_dash_templates.h | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 135b2b92f..10bd0b470 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -1,7 +1,6 @@ #ifndef _NGX_RTMP_DASH_TEMPLATES #define _NGX_RTMP_DASH_TEMPLATES - #define NGX_RTMP_DASH_MANIFEST_HEADER \ "\n" \ "\n" - #define NGX_RTMP_DASH_MANIFEST_PERIOD \ " \n" @@ -37,7 +35,6 @@ " maxFrameRate=\"%s\"\n" \ " par=\"%ui:%ui\">\n" - #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ " \n" \ " \n" - #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO \ " \n" - #define NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO \ " \n" \ " \n" - #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \ " \n" \ " \n" \ " \n" - #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER \ " \n" - #define NGX_RTMP_DASH_MANIFEST_TIME \ " \n" +#define NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION \ + " \n" #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO \ " \n" - #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO \ " \n" \ " \n" - #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER \ " \n" \ " \n" \ " \n" - #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER \ " \n" - #define NGX_RTMP_DASH_PERIOD_FOOTER \ " \n" - #define NGX_RTMP_DASH_MANIFEST_CLOCK \ " \n" - #define NGX_RTMP_DASH_MANIFEST_FOOTER \ "\n" - #endif From 130647522b8103537b3bb07fae8e1b836f952f6e Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 26 Apr 2018 10:54:00 +0000 Subject: [PATCH 046/113] Add repetition to manifest in order to reduce their size --- dash/ngx_rtmp_dash_module.c | 75 +++++++++++++++++++++++++++++----- dash/ngx_rtmp_dash_templates.h | 21 ++++++++++ 2 files changed, 86 insertions(+), 10 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 4cb0f26bd..00086fc4d 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -133,6 +133,7 @@ typedef struct { ngx_msec_t fraglen; ngx_msec_t playlen; ngx_flag_t nested; + ngx_flag_t repetition; ngx_uint_t clock_compensation; // Try to compensate clock drift // between client and server (on client side) ngx_str_t clock_helper_uri; // Use uri to static file on HTTP server @@ -191,6 +192,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, nested), NULL }, + { ngx_string("dash_repetition"), + 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_dash_app_conf_t, repetition), + NULL }, + { ngx_string("dash_clock_compensation"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -313,6 +321,59 @@ ngx_rtmp_dash_gcd(ngx_uint_t m, ngx_uint_t n) } +static u_char * +ngx_rtmp_dash_write_segment(u_char *p, u_char *last, ngx_uint_t t, + ngx_uint_t d, ngx_uint_t r) +{ + if (r == 0) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, t, d); + } else { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION, t, d, r); + } + + return p; +} + + +static u_char * +ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t *ctx, + ngx_rtmp_dash_app_conf_t *dacf, u_char *p, u_char *last) +{ + ngx_uint_t i, t, d, r; + ngx_rtmp_dash_frag_t *f; + + for (i = 0; i < ctx->nfrags; i++) { + f = ngx_rtmp_dash_get_frag(s, i); + + if (dacf->repetition) { + if (i == 0) { + t = f->timestamp; + d = f->duration; + r = 0; + } else { + if (f->duration == d) { + r++; + } else { + p = ngx_rtmp_dash_write_segment(p, last, t, d, r); + t = f->timestamp; + d = f->duration; + r = 0; + } + } + if (i == ctx->nfrags - 1) { + p = ngx_rtmp_dash_write_segment(p, last, t, d, r); + } + } else { + t = f->timestamp; + d = f->duration; + p = ngx_rtmp_dash_write_segment(p, last, t, d, 0); + } + } + + return p; +} + + static ngx_int_t ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) { @@ -850,11 +911,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) n = ngx_write_fd(fd, buffer, p - buffer); p = buffer; - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); - } + p = ngx_rtmp_dash_write_segment_timeline(s, ctx, dacf, p, last); ngx_write_fd(fds, buffer, p - buffer); @@ -877,11 +934,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) name, sep, name, sep); - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); - } + p = ngx_rtmp_dash_write_segment_timeline(s, ctx, dacf, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); @@ -2455,6 +2508,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) conf->playlen = NGX_CONF_UNSET_MSEC; conf->cleanup = NGX_CONF_UNSET; conf->nested = NGX_CONF_UNSET; + conf->repetition = NGX_CONF_UNSET; conf->clock_compensation = NGX_CONF_UNSET; conf->ad_markers = NGX_CONF_UNSET; @@ -2474,6 +2528,7 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_msec_value(conf->playlen, prev->playlen, 30000); ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1); ngx_conf_merge_value(conf->nested, prev->nested, 0); + ngx_conf_merge_value(conf->repetition, prev->repetition, 0); ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 10bd0b470..aa4571736 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -1,6 +1,7 @@ #ifndef _NGX_RTMP_DASH_TEMPLATES #define _NGX_RTMP_DASH_TEMPLATES + #define NGX_RTMP_DASH_MANIFEST_HEADER \ "\n" \ "\n" + #define NGX_RTMP_DASH_MANIFEST_PERIOD \ " \n" + #define NGX_RTMP_DASH_INBAND_EVENT \ " \n" + #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ " \n" + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ " \n" \ " \n" + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO \ " \n" + #define NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO \ " \n" \ " \n" + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO_FOOTER \ " \n" \ " \n" \ " \n" + #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO_FOOTER \ " \n" + #define NGX_RTMP_DASH_MANIFEST_TIME \ " \n" + #define NGX_RTMP_DASH_MANIFEST_TIME_WITH_REPETITION \ " \n" + #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO \ " \n" + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO \ " \n" \ " \n" + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER \ " \n" \ " \n" \ " \n" + #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO_FOOTER \ " \n" + #define NGX_RTMP_DASH_PERIOD_FOOTER \ " \n" + #define NGX_RTMP_DASH_MANIFEST_CLOCK \ " \n" + #define NGX_RTMP_DASH_MANIFEST_FOOTER \ "\n" + #endif From 5dc33e79411893ae6188030129cd7dc4742fb255 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 26 Apr 2018 17:09:08 +0000 Subject: [PATCH 047/113] Add a fallback in scte logic --- dash/ngx_rtmp_dash_module.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 00086fc4d..faef91fbf 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1158,7 +1158,10 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) pos = b.last; b.last = pos; ctx->end_cuepoint = 0; - } + } else if (ctx->end_cuepoint && ctx->cuepoint_endtime < t->earliest_pres_time ) { + /* fallback */ + ctx->end_cuepoint = 0; + } ngx_rtmp_mp4_write_styp(&b); From 3ad4e88dab61db2a89f0b6eb6a738347f3de63e8 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 26 Apr 2018 18:49:34 +0000 Subject: [PATCH 048/113] Better including start and end of the current fragment --- dash/ngx_rtmp_dash_module.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index faef91fbf..c25ed2a73 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1140,8 +1140,8 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) ctx->cuepoint_duration = 0; ctx->end_cuepoint = 1; - } else if (ctx->end_cuepoint && ctx->cuepoint_endtime > t->earliest_pres_time - && ctx->cuepoint_endtime < t->latest_pres_time) { + } else if (ctx->end_cuepoint && ctx->cuepoint_endtime >= t->earliest_pres_time + && ctx->cuepoint_endtime <= t->latest_pres_time) { ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "dash : onCuepoint write end emsg : epts='%uD', lpts='%uD', cpts='%uD', "\ From eed82c480db3cbad43093ded8f6116d9df7cb70e Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 27 Apr 2018 10:51:42 +0000 Subject: [PATCH 049/113] Also add repetition for audio in variant/master manifest --- dash/ngx_rtmp_dash_module.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index c25ed2a73..69e0e6bb6 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -628,11 +628,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) &ctx->name, sep, &ctx->name, sep); - for (i = 0; i < ctx->nfrags; i++) { - f = ngx_rtmp_dash_get_frag(s, i); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, - f->timestamp, f->duration); - } + p = ngx_rtmp_dash_write_segment_timeline(s, ctx, dacf, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO_FOOTER); From 7612f6bae8d92ae858eba55db0bb82794baa71f2 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 27 Apr 2018 12:23:05 +0000 Subject: [PATCH 050/113] Preleminary work on aes-ctr ; mess for now --- dash/ngx_rtmp_mp4_aes_ctr.c | 215 ++++++++++++++++++++++++++++++++++++ dash/ngx_rtmp_mp4_aes_ctr.h | 22 ++++ 2 files changed, 237 insertions(+) create mode 100644 dash/ngx_rtmp_mp4_aes_ctr.c create mode 100644 dash/ngx_rtmp_mp4_aes_ctr.h diff --git a/dash/ngx_rtmp_mp4_aes_ctr.c b/dash/ngx_rtmp_mp4_aes_ctr.c new file mode 100644 index 000000000..05650bd22 --- /dev/null +++ b/dash/ngx_rtmp_mp4_aes_ctr.c @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include + + +struct ctr_state { + EVP_CIPHER_CTX* cipher; + unsigned char ivec[AES_BLOCK_SIZE]; + unsigned int num; + unsigned char ecount[AES_BLOCK_SIZE]; +}; + + +static void AES_ctr128_inc(unsigned char *counter) { + unsigned char* cur_pos; + + for (cur_pos = counter + 15; cur_pos >= counter; cur_pos--) { + (*cur_pos)++; + if (*cur_pos != 0) { + break; + } + } +} + + +void print_hex(unsigned char *c) { + for(int i = 0; i < 16; i++) { + printf("%02X.", c[i]); + } + printf("\n"); +} + +void init_ctr(struct ctr_state *state, unsigned char iv[16], unsigned char* key) { + state->num = 0; + memset(state->ecount, 0, 16); + memset(state->ivec + 8, 0, 8); + memcpy(state->ivec, iv, 8); + state->cipher = EVP_CIPHER_CTX_new(); + EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ecb(), NULL, key, NULL); +} + +vod_status_t +mp4_aes_ctr_init( + mp4_aes_ctr_state_t* state, + request_context_t* request_context, + u_char* key) +{ + vod_pool_cleanup_t *cln; + + state->request_context = request_context; + + cln = vod_pool_cleanup_add(request_context->pool, 0); + if (cln == NULL) + { + vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, + "mp4_aes_ctr_init: vod_pool_cleanup_add failed"); + return VOD_ALLOC_FAILED; + } + + state->cipher = EVP_CIPHER_CTX_new(); + if (state->cipher == NULL) + { + vod_log_error(VOD_LOG_ERR, request_context->log, 0, + "mp4_aes_ctr_init: EVP_CIPHER_CTX_new failed"); + return VOD_ALLOC_FAILED; + } + + cln->handler = (vod_pool_cleanup_pt)mp4_aes_ctr_cleanup; + cln->data = state; + + if (1 != EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ecb(), NULL, key, NULL)) + { + vod_log_error(VOD_LOG_ERR, request_context->log, 0, + "mp4_aes_ctr_init: EVP_EncryptInit_ex failed"); + return VOD_ALLOC_FAILED; + } + + return VOD_OK; +} + +void +mp4_aes_ctr_set_iv( + mp4_aes_ctr_state_t* state, + u_char* iv) +{ + vod_memcpy(state->counter, iv, MP4_AES_CTR_IV_SIZE); + vod_memzero(state->counter + MP4_AES_CTR_IV_SIZE, sizeof(state->counter) - MP4_AES_CTR_IV_SIZE); + state->encrypted_pos = NULL; + state->encrypted_end = NULL; +} + +void AES_ctr128_EVPencrypt(EVP_CIPHER_CTX* cipher, const unsigned char *in, unsigned char *out, + const unsigned long length, + unsigned char counter[AES_BLOCK_SIZE], + unsigned char ecount_buf[AES_BLOCK_SIZE], + unsigned int *num) { + + int nb; + unsigned int n; + unsigned long l=length; + + n = *num; + + while (l--) { + if (n == 0) { + EVP_EncryptUpdate(cipher, ecount_buf, &nb, counter, AES_BLOCK_SIZE); + AES_ctr128_inc(counter); + } + *(out++) = *(in++) ^ ecount_buf[n]; + n = (n+1) % AES_BLOCK_SIZE; + } + + *num=n; +} + +void fencrypt(char* read, char* write, unsigned char* enc_key) { + + FILE *readFile; + FILE *writeFile; + + int bytes_read; + unsigned char indata[AES_BLOCK_SIZE]; + unsigned char outdata[AES_BLOCK_SIZE]; + unsigned char iv[AES_BLOCK_SIZE]; + + struct ctr_state state; + + RAND_bytes(iv, AES_BLOCK_SIZE); + //unsigned char *iv = (unsigned char *)"0123456789012345"; + + readFile = fopen(read,"rb"); + writeFile = fopen(write,"wb"); + + fwrite(iv, 1, AES_BLOCK_SIZE, writeFile); + //printf("saved IVec : "); + //print_hex(iv); + + init_ctr(&state, iv, enc_key); + //printf("first IVec : "); + //print_hex(state.ivec); + + while(1) { + bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile); + //printf("read data : "); + //print_hex(indata); + + AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num); + + //printf("encrypt IVec : "); + //print_hex(state.ivec); + //printf("encrypt count : "); + //print_hex(state.ecount); + //printf("write data : "); + //print_hex(outdata); + + fwrite(outdata, 1, bytes_read, writeFile); + if (bytes_read < AES_BLOCK_SIZE) { + break; + } + } + + fclose(writeFile); + fclose(readFile); +} + +void fdecrypt(char* read, char* write, unsigned char* enc_key) { + + FILE *readFile; + FILE *writeFile; + + int bytes_read; + unsigned char indata[AES_BLOCK_SIZE]; + unsigned char outdata[AES_BLOCK_SIZE]; + unsigned char iv[AES_BLOCK_SIZE]; + + struct ctr_state state; + + readFile = fopen(read,"rb"); + writeFile = fopen(write,"wb"); + + fread(iv, 1, AES_BLOCK_SIZE, readFile); + //printf("read IV : "); + //print_hex(iv); + + init_ctr(&state, iv, enc_key); + //printf("first IVec : "); + //print_hex(state.ivec); + + while(1) { + bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile); + //printf("read data : "); + //print_hex(indata); + + AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num); + + //printf("decrypt IVec : "); + //print_hex(state.ivec); + //printf("decrypt count : "); + //print_hex(state.ecount); + //printf("write data : "); + //print_hex(outdata); + + fwrite(outdata, 1, bytes_read, writeFile); + if (bytes_read < AES_BLOCK_SIZE) { + break; + } + } + + fclose(writeFile); + fclose(readFile); +} + diff --git a/dash/ngx_rtmp_mp4_aes_ctr.h b/dash/ngx_rtmp_mp4_aes_ctr.h new file mode 100644 index 000000000..4f3d98059 --- /dev/null +++ b/dash/ngx_rtmp_mp4_aes_ctr.h @@ -0,0 +1,22 @@ +#ifndef _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ +#define _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ + +#define NGX_RTMP_MP4_AES_CTR_KEY_SIZE (16) +#define NGX_RTMP_MP4_AES_CTR_IV_SIZE (8) +#define NGX_RTMP_MP4_AES_CTR_COUNTER_BUFFER_SIZE (AES_BLOCK_SIZE * 64) + +typedef struct { + EVP_CIPHER_CTX* cipher; + u_char ivec[AES_BLOCK_SIZE]; + ngx_uint_t num; + u_char ecount[AES_BLOCK_SIZE]; +} ngx_rtmp_mp4_aes_crt_state; + + +ngx_int_t ngx_rtmp_mp4_mp4_aes_ctr_init(ngx_rtmp_mp4_aes_ctr_state* state, request_context_t* request_context, u_char* key); +void ngx_rtmp_mp4_aes_ctr_set_iv(ngx_rtmp_mp4_aes_ctr_state* state, u_char* iv); +ngx_int_t mp4_aes_ctr_process( mp4_aes_ctr_state_t* state, u_char* dest, const u_char* src, uint32_t size); +void mp4_aes_ctr_increment_be64(u_char* counter); +ngx_int_t mp4_aes_ctr_write_encrypted(mp4_aes_ctr_state_t* state, write_buffer_state_t* write_buffer, u_char* cur_pos, uint32_t write_size); + +#endif /* _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ */ From 9add18319c54c8df547fad8cd16ef4fd9c2f224d Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 27 Apr 2018 12:25:51 +0000 Subject: [PATCH 051/113] Correct signature of function --- dash/ngx_rtmp_mp4_aes_ctr.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/dash/ngx_rtmp_mp4_aes_ctr.h b/dash/ngx_rtmp_mp4_aes_ctr.h index 4f3d98059..d25a70c63 100644 --- a/dash/ngx_rtmp_mp4_aes_ctr.h +++ b/dash/ngx_rtmp_mp4_aes_ctr.h @@ -12,11 +12,10 @@ typedef struct { u_char ecount[AES_BLOCK_SIZE]; } ngx_rtmp_mp4_aes_crt_state; - ngx_int_t ngx_rtmp_mp4_mp4_aes_ctr_init(ngx_rtmp_mp4_aes_ctr_state* state, request_context_t* request_context, u_char* key); void ngx_rtmp_mp4_aes_ctr_set_iv(ngx_rtmp_mp4_aes_ctr_state* state, u_char* iv); -ngx_int_t mp4_aes_ctr_process( mp4_aes_ctr_state_t* state, u_char* dest, const u_char* src, uint32_t size); -void mp4_aes_ctr_increment_be64(u_char* counter); -ngx_int_t mp4_aes_ctr_write_encrypted(mp4_aes_ctr_state_t* state, write_buffer_state_t* write_buffer, u_char* cur_pos, uint32_t write_size); +ngx_int_t ngx_rtmp_mp4_aes_ctr_process(ngx_rtmp_mp4_aes_ctr_state_t* state, u_char* dest, const u_char* src, uint32_t size); +void ngx_rtmp_mp4_aes_ctr_increment_be64(u_char* counter); +ngx_int_t ngx_rtmp_mp4_aes_ctr_write_encrypted(ngx_rtmp_mp4_aes_ctr_state_t* state, write_buffer_state_t* write_buffer, u_char* cur_pos, uint32_t write_size); #endif /* _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ */ From fe48d3d5d9606cae1877148947fc9aec66e74638 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 1 May 2018 14:44:20 +0000 Subject: [PATCH 052/113] Let's try a shorter version --- config | 2 + dash/ngx_rtmp_dash_module.c | 9 +- dash/ngx_rtmp_mp4_aes_ctr.c | 242 ++++++------------------------------ dash/ngx_rtmp_mp4_aes_ctr.h | 19 +-- 4 files changed, 51 insertions(+), 221 deletions(-) diff --git a/config b/config index 18f857ef4..be4421669 100644 --- a/config +++ b/config @@ -43,6 +43,7 @@ RTMP_DEPS=" \ $ngx_addon_dir/ngx_rtmp_proxy_protocol.h \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.h \ $ngx_addon_dir/dash/ngx_rtmp_mp4.h \ + $ngx_addon_dir/dash/ngx_rtmp_mp4_aes_ctr.h \ $ngx_addon_dir/dash/ngx_rtmp_dash_templates.h \ " RTMP_CORE_SRCS=" \ @@ -79,6 +80,7 @@ RTMP_CORE_SRCS=" \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.c \ $ngx_addon_dir/hls/ngx_rtmp_mpegts_crc.c \ $ngx_addon_dir/dash/ngx_rtmp_mp4.c \ + $ngx_addon_dir/dash/ngx_rtmp_mp4_aes_ctr.c \ " RTMP_HTTP_SRCS=" \ $ngx_addon_dir/ngx_rtmp_stat_module.c \ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 69e0e6bb6..940e75ca7 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1779,12 +1779,19 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { + /* XXX Insert CENC Here ? */ if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: " ngx_write_fd_n " failed"); return NGX_ERROR; - } + } + if (t->type == 'v') { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: write_video sample : %ui", + t->sample_count); + } + smpl = &t->samples[t->sample_count]; smpl->delay = delay; diff --git a/dash/ngx_rtmp_mp4_aes_ctr.c b/dash/ngx_rtmp_mp4_aes_ctr.c index 05650bd22..1d68d5bbd 100644 --- a/dash/ngx_rtmp_mp4_aes_ctr.c +++ b/dash/ngx_rtmp_mp4_aes_ctr.c @@ -1,215 +1,49 @@ + + #include #include #include -#include -#include -#include - - -struct ctr_state { - EVP_CIPHER_CTX* cipher; - unsigned char ivec[AES_BLOCK_SIZE]; - unsigned int num; - unsigned char ecount[AES_BLOCK_SIZE]; -}; - - -static void AES_ctr128_inc(unsigned char *counter) { - unsigned char* cur_pos; - - for (cur_pos = counter + 15; cur_pos >= counter; cur_pos--) { - (*cur_pos)++; - if (*cur_pos != 0) { - break; - } - } -} - - -void print_hex(unsigned char *c) { - for(int i = 0; i < 16; i++) { - printf("%02X.", c[i]); - } - printf("\n"); -} - -void init_ctr(struct ctr_state *state, unsigned char iv[16], unsigned char* key) { - state->num = 0; - memset(state->ecount, 0, 16); - memset(state->ivec + 8, 0, 8); - memcpy(state->ivec, iv, 8); - state->cipher = EVP_CIPHER_CTX_new(); - EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ecb(), NULL, key, NULL); -} - -vod_status_t -mp4_aes_ctr_init( - mp4_aes_ctr_state_t* state, - request_context_t* request_context, - u_char* key) -{ - vod_pool_cleanup_t *cln; - - state->request_context = request_context; - - cln = vod_pool_cleanup_add(request_context->pool, 0); - if (cln == NULL) - { - vod_log_debug0(VOD_LOG_DEBUG_LEVEL, request_context->log, 0, - "mp4_aes_ctr_init: vod_pool_cleanup_add failed"); - return VOD_ALLOC_FAILED; - } - - state->cipher = EVP_CIPHER_CTX_new(); - if (state->cipher == NULL) - { - vod_log_error(VOD_LOG_ERR, request_context->log, 0, - "mp4_aes_ctr_init: EVP_CIPHER_CTX_new failed"); - return VOD_ALLOC_FAILED; - } - - cln->handler = (vod_pool_cleanup_pt)mp4_aes_ctr_cleanup; - cln->data = state; - - if (1 != EVP_EncryptInit_ex(state->cipher, EVP_aes_128_ecb(), NULL, key, NULL)) - { - vod_log_error(VOD_LOG_ERR, request_context->log, 0, - "mp4_aes_ctr_init: EVP_EncryptInit_ex failed"); - return VOD_ALLOC_FAILED; - } - - return VOD_OK; -} - -void -mp4_aes_ctr_set_iv( - mp4_aes_ctr_state_t* state, - u_char* iv) -{ - vod_memcpy(state->counter, iv, MP4_AES_CTR_IV_SIZE); - vod_memzero(state->counter + MP4_AES_CTR_IV_SIZE, sizeof(state->counter) - MP4_AES_CTR_IV_SIZE); - state->encrypted_pos = NULL; - state->encrypted_end = NULL; -} +#include +#include +#include +#include "ngx_rtmp_mp4_aes_ctr.h" -void AES_ctr128_EVPencrypt(EVP_CIPHER_CTX* cipher, const unsigned char *in, unsigned char *out, - const unsigned long length, - unsigned char counter[AES_BLOCK_SIZE], - unsigned char ecount_buf[AES_BLOCK_SIZE], - unsigned int *num) { - int nb; - unsigned int n; - unsigned long l=length; +static ngx_int_t ngx_rtmp_mp4_aes_ctr_encrypt(const uint8_t *key, const uint8_t *nonce, uint8_t *data, size_t data_len) { - n = *num; - - while (l--) { - if (n == 0) { - EVP_EncryptUpdate(cipher, ecount_buf, &nb, counter, AES_BLOCK_SIZE); - AES_ctr128_inc(counter); + EVP_CIPHER_CTX* cipher; + size_t j, len, left = data_len; + int i, w; + uint8_t *pos = data; + uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; + + ngx_memset(counter + 8, 0, 8); + ngx_memcpy(counter, nonce, 8); + + cipher = EVP_CIPHER_CTX_new(); + /* test return NGX_ERROR */ + EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL); + /* test */ + + while (left > 0) { + EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); + + len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; + for (j = 0; j < len; j++) + pos[j] ^= buf[j]; + pos += len; + left -= len; + + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } } - *(out++) = *(in++) ^ ecount_buf[n]; - n = (n+1) % AES_BLOCK_SIZE; - } - - *num=n; -} - -void fencrypt(char* read, char* write, unsigned char* enc_key) { - - FILE *readFile; - FILE *writeFile; - - int bytes_read; - unsigned char indata[AES_BLOCK_SIZE]; - unsigned char outdata[AES_BLOCK_SIZE]; - unsigned char iv[AES_BLOCK_SIZE]; - - struct ctr_state state; - - RAND_bytes(iv, AES_BLOCK_SIZE); - //unsigned char *iv = (unsigned char *)"0123456789012345"; - - readFile = fopen(read,"rb"); - writeFile = fopen(write,"wb"); - - fwrite(iv, 1, AES_BLOCK_SIZE, writeFile); - //printf("saved IVec : "); - //print_hex(iv); - - init_ctr(&state, iv, enc_key); - //printf("first IVec : "); - //print_hex(state.ivec); - - while(1) { - bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile); - //printf("read data : "); - //print_hex(indata); - - AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num); - //printf("encrypt IVec : "); - //print_hex(state.ivec); - //printf("encrypt count : "); - //print_hex(state.ecount); - //printf("write data : "); - //print_hex(outdata); + EVP_CIPHER_CTX_free(cipher); - fwrite(outdata, 1, bytes_read, writeFile); - if (bytes_read < AES_BLOCK_SIZE) { - break; - } - } - - fclose(writeFile); - fclose(readFile); + return NGX_OK; } + -void fdecrypt(char* read, char* write, unsigned char* enc_key) { - - FILE *readFile; - FILE *writeFile; - - int bytes_read; - unsigned char indata[AES_BLOCK_SIZE]; - unsigned char outdata[AES_BLOCK_SIZE]; - unsigned char iv[AES_BLOCK_SIZE]; - - struct ctr_state state; - - readFile = fopen(read,"rb"); - writeFile = fopen(write,"wb"); - - fread(iv, 1, AES_BLOCK_SIZE, readFile); - //printf("read IV : "); - //print_hex(iv); - - init_ctr(&state, iv, enc_key); - //printf("first IVec : "); - //print_hex(state.ivec); - - while(1) { - bytes_read = fread(indata, 1, AES_BLOCK_SIZE, readFile); - //printf("read data : "); - //print_hex(indata); - - AES_ctr128_EVPencrypt(state.cipher, indata, outdata, bytes_read, state.ivec, state.ecount, &state.num); - - //printf("decrypt IVec : "); - //print_hex(state.ivec); - //printf("decrypt count : "); - //print_hex(state.ecount); - //printf("write data : "); - //print_hex(outdata); - - fwrite(outdata, 1, bytes_read, writeFile); - if (bytes_read < AES_BLOCK_SIZE) { - break; - } - } - - fclose(writeFile); - fclose(readFile); -} - diff --git a/dash/ngx_rtmp_mp4_aes_ctr.h b/dash/ngx_rtmp_mp4_aes_ctr.h index d25a70c63..e4aaad779 100644 --- a/dash/ngx_rtmp_mp4_aes_ctr.h +++ b/dash/ngx_rtmp_mp4_aes_ctr.h @@ -1,21 +1,8 @@ #ifndef _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ #define _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ -#define NGX_RTMP_MP4_AES_CTR_KEY_SIZE (16) -#define NGX_RTMP_MP4_AES_CTR_IV_SIZE (8) -#define NGX_RTMP_MP4_AES_CTR_COUNTER_BUFFER_SIZE (AES_BLOCK_SIZE * 64) - -typedef struct { - EVP_CIPHER_CTX* cipher; - u_char ivec[AES_BLOCK_SIZE]; - ngx_uint_t num; - u_char ecount[AES_BLOCK_SIZE]; -} ngx_rtmp_mp4_aes_crt_state; - -ngx_int_t ngx_rtmp_mp4_mp4_aes_ctr_init(ngx_rtmp_mp4_aes_ctr_state* state, request_context_t* request_context, u_char* key); -void ngx_rtmp_mp4_aes_ctr_set_iv(ngx_rtmp_mp4_aes_ctr_state* state, u_char* iv); -ngx_int_t ngx_rtmp_mp4_aes_ctr_process(ngx_rtmp_mp4_aes_ctr_state_t* state, u_char* dest, const u_char* src, uint32_t size); -void ngx_rtmp_mp4_aes_ctr_increment_be64(u_char* counter); -ngx_int_t ngx_rtmp_mp4_aes_ctr_write_encrypted(ngx_rtmp_mp4_aes_ctr_state_t* state, write_buffer_state_t* write_buffer, u_char* cur_pos, uint32_t write_size); +static ngx_int_t +ngx_rtmp_mp4_aes_ctr_encrypt(const uint8_t *key, const uint8_t *nonce, + uint8_t *data, size_t data_len); #endif /* _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ */ From b216121bd41bc8f728a74af894c82cbeca44e43e Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 1 May 2018 16:49:31 +0000 Subject: [PATCH 053/113] Not mp4 specific so... --- dash/ngx_rtmp_aes_ctr.c | 61 +++++++++++++++++++++++++++++++++++++ dash/ngx_rtmp_aes_ctr.h | 12 ++++++++ dash/ngx_rtmp_dash_module.c | 20 ++++++++---- dash/ngx_rtmp_mp4_aes_ctr.c | 49 ----------------------------- dash/ngx_rtmp_mp4_aes_ctr.h | 8 ----- 5 files changed, 87 insertions(+), 63 deletions(-) create mode 100644 dash/ngx_rtmp_aes_ctr.c create mode 100644 dash/ngx_rtmp_aes_ctr.h delete mode 100644 dash/ngx_rtmp_mp4_aes_ctr.c delete mode 100644 dash/ngx_rtmp_mp4_aes_ctr.h diff --git a/dash/ngx_rtmp_aes_ctr.c b/dash/ngx_rtmp_aes_ctr.c new file mode 100644 index 000000000..c474c2a2b --- /dev/null +++ b/dash/ngx_rtmp_aes_ctr.c @@ -0,0 +1,61 @@ + + +#include +#include +#include +#include +#include +#include +#include "ngx_rtmp_aes_ctr.h" + + +ngx_int_t +ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, + const uint8_t *key, const uint8_t *nonce, + uint8_t *data, size_t data_len) +{ + + EVP_CIPHER_CTX* cipher; + size_t j, len, left = data_len; + int i, w; + uint8_t *pos = data; + uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; + + ngx_memset(counter + NGX_RTMP_MP4_AES_CTR_IV_SIZE, 0, NGX_RTMP_MP4_AES_CTR_IV_SIZE); + ngx_memcpy(counter, nonce, NGX_RTMP_MP4_AES_CTR_IV_SIZE); + + cipher = EVP_CIPHER_CTX_new(); + if (cipher == NULL) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash rtmp_aes_ctr_encrypt: evp_cipher_ctx failed"); + return NGX_ERROR; + } + + if (EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash rtmp_aes_ctr_encrypt: evp_encrypt_init failed"); + return NGX_ERROR; + } + + while (left > 0) { + EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); + + len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; + for (j = 0; j < len; j++) + pos[j] ^= buf[j]; + pos += len; + left -= len; + + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } + } + + EVP_CIPHER_CTX_free(cipher); + + return NGX_OK; +} + + diff --git a/dash/ngx_rtmp_aes_ctr.h b/dash/ngx_rtmp_aes_ctr.h new file mode 100644 index 000000000..6a586f6bb --- /dev/null +++ b/dash/ngx_rtmp_aes_ctr.h @@ -0,0 +1,12 @@ +#ifndef _NGX_RTMP_AES_CTR_H_INCLUDED_ +#define _NGX_RTMP_AES_CTR_H_INCLUDED_ + +#define NGX_RTMP_AES_CTR_IV_SIZE (8) +#define NGX_RTMP_AES_CTR_KEY_SIZE (16) + +ngx_int_t +ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, + const uint8_t *key, const uint8_t *nonce, + uint8_t *data, size_t data_len); + +#endif /* _NGX_RTMP_AES_CTR_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 940e75ca7..e9147a188 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -7,6 +7,7 @@ #include "ngx_rtmp_live_module.h" #include "ngx_rtmp_mp4.h" #include "ngx_rtmp_dash_templates.h" +#include "ngx_rtmp_aes_ctr.h" static ngx_rtmp_publish_pt next_publish; @@ -337,7 +338,7 @@ ngx_rtmp_dash_write_segment(u_char *p, u_char *last, ngx_uint_t t, static u_char * ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t *ctx, - ngx_rtmp_dash_app_conf_t *dacf, u_char *p, u_char *last) + ngx_rtmp_dash_app_conf_t *dacf, u_char *p, u_char *last) { ngx_uint_t i, t, d, r; ngx_rtmp_dash_frag_t *f; @@ -1752,9 +1753,14 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, u_char *p; size_t size, bsize; ngx_rtmp_mp4_sample_t *smpl; + u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; + u_char cenc_iv[NGX_RTMP_AES_CTR_IV_SIZE]; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + cenc_key = (uint8_t *)"0123456789abcdef"; + cenc_iv = (uint8_t *)"01234567"; + p = buffer; size = 0; @@ -1779,7 +1785,9 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { - /* XXX Insert CENC Here ? */ + /* WIP CENC */ + ngx_rtmp_aes_ctr_encrypt(s, cenc_key, cenc_iv, buffer, size); + if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: " ngx_write_fd_n " failed"); @@ -2202,7 +2210,7 @@ ngx_rtmp_dash_playlist(ngx_rtmp_session_t *s, ngx_rtmp_playlist_t *v) static ngx_int_t ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) + ngx_chain_t *in) { ngx_int_t res; ngx_rtmp_dash_ctx_t *ctx; @@ -2281,7 +2289,7 @@ ngx_rtmp_dash_on_cuepoint(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, static ngx_int_t ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) + ngx_chain_t *in) { ngx_int_t res; ngx_rtmp_dash_ctx_t *ctx; @@ -2397,7 +2405,7 @@ ngx_rtmp_dash_on_cuepoint_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, static ngx_int_t ngx_rtmp_dash_on_cuepoint_cont_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) + ngx_chain_t *in) { ngx_int_t res; @@ -2469,7 +2477,7 @@ ngx_rtmp_dash_on_cuepoint_cont_scte35(ngx_rtmp_session_t *s, ngx_rtmp_header_t * static ngx_int_t ngx_rtmp_dash_ad_markers(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, - ngx_chain_t *in) + ngx_chain_t *in) { ngx_rtmp_dash_app_conf_t *dacf; ngx_rtmp_dash_ctx_t *ctx; diff --git a/dash/ngx_rtmp_mp4_aes_ctr.c b/dash/ngx_rtmp_mp4_aes_ctr.c deleted file mode 100644 index 1d68d5bbd..000000000 --- a/dash/ngx_rtmp_mp4_aes_ctr.c +++ /dev/null @@ -1,49 +0,0 @@ - - -#include -#include -#include -#include -#include -#include -#include "ngx_rtmp_mp4_aes_ctr.h" - - -static ngx_int_t ngx_rtmp_mp4_aes_ctr_encrypt(const uint8_t *key, const uint8_t *nonce, uint8_t *data, size_t data_len) { - - EVP_CIPHER_CTX* cipher; - size_t j, len, left = data_len; - int i, w; - uint8_t *pos = data; - uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; - - ngx_memset(counter + 8, 0, 8); - ngx_memcpy(counter, nonce, 8); - - cipher = EVP_CIPHER_CTX_new(); - /* test return NGX_ERROR */ - EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL); - /* test */ - - while (left > 0) { - EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); - - len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; - for (j = 0; j < len; j++) - pos[j] ^= buf[j]; - pos += len; - left -= len; - - for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { - counter[i]++; - if (counter[i]) - break; - } - } - - EVP_CIPHER_CTX_free(cipher); - - return NGX_OK; -} - - diff --git a/dash/ngx_rtmp_mp4_aes_ctr.h b/dash/ngx_rtmp_mp4_aes_ctr.h deleted file mode 100644 index e4aaad779..000000000 --- a/dash/ngx_rtmp_mp4_aes_ctr.h +++ /dev/null @@ -1,8 +0,0 @@ -#ifndef _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ -#define _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ - -static ngx_int_t -ngx_rtmp_mp4_aes_ctr_encrypt(const uint8_t *key, const uint8_t *nonce, - uint8_t *data, size_t data_len); - -#endif /* _NGX_RTMP_MP4_AES_CTR_H_INCLUDED_ */ From 21df9ebd9558331c5597b12144f9c96639d13edd Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 1 May 2018 19:27:33 +0000 Subject: [PATCH 054/113] Print counter in hex to debug --- config | 4 ++-- dash/ngx_rtmp_aes_ctr.c | 27 +++++++++++++++++++++------ dash/ngx_rtmp_dash_module.c | 8 ++++---- 3 files changed, 27 insertions(+), 12 deletions(-) diff --git a/config b/config index be4421669..baf15fb88 100644 --- a/config +++ b/config @@ -43,7 +43,7 @@ RTMP_DEPS=" \ $ngx_addon_dir/ngx_rtmp_proxy_protocol.h \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.h \ $ngx_addon_dir/dash/ngx_rtmp_mp4.h \ - $ngx_addon_dir/dash/ngx_rtmp_mp4_aes_ctr.h \ + $ngx_addon_dir/dash/ngx_rtmp_aes_ctr.h \ $ngx_addon_dir/dash/ngx_rtmp_dash_templates.h \ " RTMP_CORE_SRCS=" \ @@ -80,7 +80,7 @@ RTMP_CORE_SRCS=" \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.c \ $ngx_addon_dir/hls/ngx_rtmp_mpegts_crc.c \ $ngx_addon_dir/dash/ngx_rtmp_mp4.c \ - $ngx_addon_dir/dash/ngx_rtmp_mp4_aes_ctr.c \ + $ngx_addon_dir/dash/ngx_rtmp_aes_ctr.c \ " RTMP_HTTP_SRCS=" \ $ngx_addon_dir/ngx_rtmp_stat_module.c \ diff --git a/dash/ngx_rtmp_aes_ctr.c b/dash/ngx_rtmp_aes_ctr.c index c474c2a2b..8690d0c94 100644 --- a/dash/ngx_rtmp_aes_ctr.c +++ b/dash/ngx_rtmp_aes_ctr.c @@ -9,6 +9,19 @@ #include "ngx_rtmp_aes_ctr.h" +void +print_counter(ngx_rtmp_session_t *s, uint8_t *c, size_t l) +{ + u_char hex[AES_BLOCK_SIZE*2+1]; + + ngx_hex_dump(hex, c, AES_BLOCK_SIZE); + hex[AES_BLOCK_SIZE*2] = '\0'; + + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash aes_counter: %ui %s", l, hex); +} + + ngx_int_t ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, const uint8_t *key, const uint8_t *nonce, @@ -16,13 +29,13 @@ ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, { EVP_CIPHER_CTX* cipher; - size_t j, len, left = data_len; - int i, w; - uint8_t *pos = data; - uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; + size_t j, len, left = data_len; + int i, w; + uint8_t *pos = data; + uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; - ngx_memset(counter + NGX_RTMP_MP4_AES_CTR_IV_SIZE, 0, NGX_RTMP_MP4_AES_CTR_IV_SIZE); - ngx_memcpy(counter, nonce, NGX_RTMP_MP4_AES_CTR_IV_SIZE); + ngx_memset(counter + NGX_RTMP_AES_CTR_IV_SIZE, 0, NGX_RTMP_AES_CTR_IV_SIZE); + ngx_memcpy(counter, nonce, NGX_RTMP_AES_CTR_IV_SIZE); cipher = EVP_CIPHER_CTX_new(); if (cipher == NULL) { @@ -38,6 +51,8 @@ ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, } while (left > 0) { + + print_counter(s, counter, left); EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e9147a188..06918c6a1 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1753,13 +1753,13 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, u_char *p; size_t size, bsize; ngx_rtmp_mp4_sample_t *smpl; - u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; - u_char cenc_iv[NGX_RTMP_AES_CTR_IV_SIZE]; + static u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; + static u_char cenc_iv[NGX_RTMP_AES_CTR_IV_SIZE]; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - cenc_key = (uint8_t *)"0123456789abcdef"; - cenc_iv = (uint8_t *)"01234567"; + ngx_cpymem(cenc_key, (u_char *)"0123456789abcdef", NGX_RTMP_AES_CTR_KEY_SIZE); + ngx_cpymem(cenc_iv, (u_char *)"deadbeaf", NGX_RTMP_AES_CTR_IV_SIZE); p = buffer; size = 0; From c66c132e2d927e089abcf8781655117de14183c8 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 4 May 2018 17:02:44 +0000 Subject: [PATCH 055/113] Change sample structure --- dash/ngx_rtmp_dash_module.c | 36 ++++++++++++++++++++++++++---------- dash/ngx_rtmp_mp4.h | 3 +++ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 06918c6a1..d0b7e4b26 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -135,6 +135,7 @@ typedef struct { ngx_msec_t playlen; ngx_flag_t nested; ngx_flag_t repetition; + ngx_flag_t cenc; ngx_uint_t clock_compensation; // Try to compensate clock drift // between client and server (on client side) ngx_str_t clock_helper_uri; // Use uri to static file on HTTP server @@ -200,6 +201,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, repetition), NULL }, + { ngx_string("dash_cenc"), + 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_dash_app_conf_t, cenc), + NULL }, + { ngx_string("dash_clock_compensation"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -1750,16 +1758,21 @@ static ngx_int_t ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, ngx_rtmp_dash_track_t *t, ngx_int_t key, uint32_t timestamp, uint32_t delay) { - u_char *p; - size_t size, bsize; - ngx_rtmp_mp4_sample_t *smpl; + u_char *p; + size_t size, bsize; + ngx_rtmp_mp4_sample_t *smpl; + ngx_rtmp_dash_app_conf_t *dacf; + + static u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; + static u_char cenc_iv[NGX_RTMP_AES_CTR_IV_SIZE]; + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - static u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; - static u_char cenc_iv[NGX_RTMP_AES_CTR_IV_SIZE]; - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - ngx_cpymem(cenc_key, (u_char *)"0123456789abcdef", NGX_RTMP_AES_CTR_KEY_SIZE); - ngx_cpymem(cenc_iv, (u_char *)"deadbeaf", NGX_RTMP_AES_CTR_IV_SIZE); + if (dacf->cenc) { + ngx_cpymem(cenc_key, (u_char *)"0123456789abcdef", NGX_RTMP_AES_CTR_KEY_SIZE); + ngx_cpymem(cenc_iv, (u_char *)"\xde\xad\xbe\xaf\xf0\x0d\xba\xad", NGX_RTMP_AES_CTR_IV_SIZE); + } p = buffer; size = 0; @@ -1785,8 +1798,9 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { - /* WIP CENC */ - ngx_rtmp_aes_ctr_encrypt(s, cenc_key, cenc_iv, buffer, size); + if (dacf->cenc) { + ngx_rtmp_aes_ctr_encrypt(s, cenc_key, cenc_iv, buffer, size); + } if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, @@ -2523,6 +2537,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) conf->cleanup = NGX_CONF_UNSET; conf->nested = NGX_CONF_UNSET; conf->repetition = NGX_CONF_UNSET; + conf->cenc = NGX_CONF_UNSET; conf->clock_compensation = NGX_CONF_UNSET; conf->ad_markers = NGX_CONF_UNSET; @@ -2543,6 +2558,7 @@ ngx_rtmp_dash_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->nested, prev->nested, 0); ngx_conf_merge_value(conf->repetition, prev->repetition, 0); + ngx_conf_merge_value(conf->cenc, prev->cenc, 0); ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index ab010213b..6fa8dccac 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -7,6 +7,7 @@ #include #include #include +#include "ngx_rtmp_aes_ctr.h" #define NGX_RTMP_MP4_SAMPLE_SIZE 0x01 @@ -21,6 +22,8 @@ typedef struct { uint32_t delay; uint32_t timestamp; unsigned key:1; + unsigned is_protected:1; + u_char iv[NGX_RTMP_AES_CTR_IV_SIZE]; } ngx_rtmp_mp4_sample_t; From e2fa64093178fc6cd1c47e44b78a508f9122fa3d Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 4 May 2018 17:12:17 +0000 Subject: [PATCH 056/113] populate these fields --- dash/ngx_rtmp_dash_module.c | 7 +++++++ dash/ngx_rtmp_mp4.h | 3 +-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index d0b7e4b26..d09f34baf 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1791,6 +1791,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, ngx_rtmp_dash_update_fragments(s, key, timestamp); if (t->sample_count == 0) { + // add a random iv ? t->earliest_pres_time = timestamp; } @@ -1798,6 +1799,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { + //update iv + 1 if (dacf->cenc) { ngx_rtmp_aes_ctr_encrypt(s, cenc_key, cenc_iv, buffer, size); } @@ -1822,6 +1824,11 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, smpl->timestamp = timestamp; smpl->key = (key ? 1 : 0); + if (dacf->cenc) { + smpl->is_protected = 1; + smpl->iv = cenc_iv; + } + if (t->sample_count > 0) { smpl = &t->samples[t->sample_count - 1]; smpl->duration = timestamp - smpl->timestamp; diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 6fa8dccac..5f929d885 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -7,7 +7,6 @@ #include #include #include -#include "ngx_rtmp_aes_ctr.h" #define NGX_RTMP_MP4_SAMPLE_SIZE 0x01 @@ -23,7 +22,7 @@ typedef struct { uint32_t timestamp; unsigned key:1; unsigned is_protected:1; - u_char iv[NGX_RTMP_AES_CTR_IV_SIZE]; + u_char *iv; } ngx_rtmp_mp4_sample_t; From 23738a8f8255b0aaea9d477db77c3a9a54a562cd Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 6 May 2018 11:37:29 +0000 Subject: [PATCH 057/113] Add function to increment 8bytes IV --- dash/ngx_rtmp_aes_ctr.c | 13 +++++++++++++ dash/ngx_rtmp_aes_ctr.h | 3 +++ dash/ngx_rtmp_dash_module.c | 6 +++--- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_aes_ctr.c b/dash/ngx_rtmp_aes_ctr.c index 8690d0c94..3e9b37a23 100644 --- a/dash/ngx_rtmp_aes_ctr.c +++ b/dash/ngx_rtmp_aes_ctr.c @@ -22,6 +22,19 @@ print_counter(ngx_rtmp_session_t *s, uint8_t *c, size_t l) } +void +ngx_rtmp_aes_increment_iv(u_char* counter) +{ + int i; + + for (i = NGX_RTMP_AES_CTR_IV_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } +} + + ngx_int_t ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, const uint8_t *key, const uint8_t *nonce, diff --git a/dash/ngx_rtmp_aes_ctr.h b/dash/ngx_rtmp_aes_ctr.h index 6a586f6bb..f67cea675 100644 --- a/dash/ngx_rtmp_aes_ctr.h +++ b/dash/ngx_rtmp_aes_ctr.h @@ -4,6 +4,9 @@ #define NGX_RTMP_AES_CTR_IV_SIZE (8) #define NGX_RTMP_AES_CTR_KEY_SIZE (16) +void +ngx_rtmp_aes_increment_iv(u_char* counter); + ngx_int_t ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, const uint8_t *key, const uint8_t *nonce, diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index d09f34baf..7c376384a 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1811,9 +1811,9 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, } if (t->type == 'v') { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: write_video sample : %ui", - t->sample_count); + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: write_video sample: count=%ui, key=%ui, size=%ui", + t->sample_count, key, size); } smpl = &t->samples[t->sample_count]; From f2068e10a4827823abc08131a3183670e507e904 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 7 May 2018 20:41:54 +0000 Subject: [PATCH 058/113] Add some initialisation to iv --- dash/ngx_rtmp_aes_ctr.c | 21 ++++++++++++++++----- dash/ngx_rtmp_aes_ctr.h | 6 +++++- dash/ngx_rtmp_dash_module.c | 28 +++++++++++++++++----------- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/dash/ngx_rtmp_aes_ctr.c b/dash/ngx_rtmp_aes_ctr.c index 3e9b37a23..70be3307e 100644 --- a/dash/ngx_rtmp_aes_ctr.c +++ b/dash/ngx_rtmp_aes_ctr.c @@ -22,14 +22,25 @@ print_counter(ngx_rtmp_session_t *s, uint8_t *c, size_t l) } +ngx_int_t +ngx_rtmp_aes_rand_iv(u_char* iv) +{ + if(RAND_bytes(iv, NGX_RTMP_AES_CTR_IV_SIZE) != 1) { + return NGX_ERROR; + } + + return NGX_OK; +} + + void -ngx_rtmp_aes_increment_iv(u_char* counter) +ngx_rtmp_aes_increment_iv(u_char* iv) { int i; for (i = NGX_RTMP_AES_CTR_IV_SIZE - 1; i >= 0; i--) { - counter[i]++; - if (counter[i]) + iv[i]++; + if (iv[i]) break; } } @@ -37,7 +48,7 @@ ngx_rtmp_aes_increment_iv(u_char* counter) ngx_int_t ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, - const uint8_t *key, const uint8_t *nonce, + const uint8_t *key, const uint8_t *iv, uint8_t *data, size_t data_len) { @@ -48,7 +59,7 @@ ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; ngx_memset(counter + NGX_RTMP_AES_CTR_IV_SIZE, 0, NGX_RTMP_AES_CTR_IV_SIZE); - ngx_memcpy(counter, nonce, NGX_RTMP_AES_CTR_IV_SIZE); + ngx_memcpy(counter, iv, NGX_RTMP_AES_CTR_IV_SIZE); cipher = EVP_CIPHER_CTX_new(); if (cipher == NULL) { diff --git a/dash/ngx_rtmp_aes_ctr.h b/dash/ngx_rtmp_aes_ctr.h index f67cea675..98a75dc0a 100644 --- a/dash/ngx_rtmp_aes_ctr.h +++ b/dash/ngx_rtmp_aes_ctr.h @@ -4,8 +4,12 @@ #define NGX_RTMP_AES_CTR_IV_SIZE (8) #define NGX_RTMP_AES_CTR_KEY_SIZE (16) + +ngx_int_t +ngx_rtmp_aes_rand_iv(u_char* iv); + void -ngx_rtmp_aes_increment_iv(u_char* counter); +ngx_rtmp_aes_increment_iv(u_char* iv); ngx_int_t ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 7c376384a..911d13abc 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -50,6 +50,7 @@ typedef struct { char type; uint32_t earliest_pres_time; uint32_t latest_pres_time; + unsigned is_protected:1; ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; } ngx_rtmp_dash_track_t; @@ -1278,7 +1279,8 @@ static ngx_int_t ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, ngx_uint_t id, char type) { - ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; if (t->opened) { return NGX_OK; @@ -1288,6 +1290,7 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, "dash: open fragment id=%ui, type='%c'", id, type); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); *ngx_sprintf(ctx->stream.data + ctx->stream.len, "raw.m4%c", type) = 0; @@ -1307,6 +1310,10 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, t->latest_pres_time = 0; t->mdat_size = 0; t->opened = 1; + + if (dacf->cenc) { + t->is_protected = 1; + } if (type == 'v') { t->sample_mask = NGX_RTMP_MP4_SAMPLE_SIZE| @@ -1702,7 +1709,6 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, } else { /* sometimes clients generate slightly unordered frames */ - hit = (-d > 1000); } @@ -1761,17 +1767,14 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, u_char *p; size_t size, bsize; ngx_rtmp_mp4_sample_t *smpl; - ngx_rtmp_dash_app_conf_t *dacf; static u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; static u_char cenc_iv[NGX_RTMP_AES_CTR_IV_SIZE]; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - - if (dacf->cenc) { + if (t->is_protected) { ngx_cpymem(cenc_key, (u_char *)"0123456789abcdef", NGX_RTMP_AES_CTR_KEY_SIZE); - ngx_cpymem(cenc_iv, (u_char *)"\xde\xad\xbe\xaf\xf0\x0d\xba\xad", NGX_RTMP_AES_CTR_IV_SIZE); + //ngx_cpymem(cenc_iv, (u_char *)"\xde\xad\xbe\xaf\xf0\x0d\xba\xad", NGX_RTMP_AES_CTR_IV_SIZE); } p = buffer; @@ -1791,17 +1794,19 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, ngx_rtmp_dash_update_fragments(s, key, timestamp); if (t->sample_count == 0) { - // add a random iv ? t->earliest_pres_time = timestamp; + if (t->is_protected) { + ngx_rtmp_aes_rand_iv(cenc_iv); + } } t->latest_pres_time = timestamp; if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { - //update iv + 1 - if (dacf->cenc) { + if (t->is_protected) { ngx_rtmp_aes_ctr_encrypt(s, cenc_key, cenc_iv, buffer, size); + ngx_rtmp_aes_increment_iv(cenc_iv); } if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { @@ -1824,7 +1829,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, smpl->timestamp = timestamp; smpl->key = (key ? 1 : 0); - if (dacf->cenc) { + if (t->is_protected) { smpl->is_protected = 1; smpl->iv = cenc_iv; } @@ -1921,6 +1926,7 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_ERROR; } + /* check what header it is */ ftype = (in->buf->pos[0] & 0xf0) >> 4; /* skip AVC config */ From af388c9e2789e34a732b1beb90f5d64e2e886cda Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 10 May 2018 16:51:39 +0000 Subject: [PATCH 059/113] Signalisation mess round one saiz saio and senc --- dash/ngx_rtmp_dash_module.c | 2 +- dash/ngx_rtmp_mp4.c | 101 +++++++++++++++++++++++++++++++++--- dash/ngx_rtmp_mp4.h | 3 +- 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 911d13abc..597638117 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1175,7 +1175,7 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) b.last += 44; /* leave room for sidx */ ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, - t->samples, t->sample_mask, t->id); + t->samples, t->sample_mask, t->id, t->is_protected); pos1 = b.last; b.last = pos; diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 34745e181..1c3b233ba 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -986,7 +986,8 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, uint32_t earliest_pres_time) static ngx_int_t ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, - ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, u_char *moof_pos) + ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, + u_char *moof_pos, ngx_flag_t is_protected) { u_char *pos; uint32_t i, offset, nitems, flags; @@ -1018,7 +1019,16 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, flags |= 0x000800; } - offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8; + if (is_protected) { + /* if cenc is enabled we neeed to add + * saiz(17) saio(20) senc(16 + sc*8) + * to the data offset */ + offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4); + offset += 17 + 20 + 16 + (sample_count * NGX_RTMP_AES_CTR_IV_SIZE) + 8; + + } else { + offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8; + } ngx_rtmp_mp4_field_32(b, flags); ngx_rtmp_mp4_field_32(b, sample_count); @@ -1049,10 +1059,82 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, } +static ngx_int_t +ngx_rtmp_mp4_write_saiz(ngx_buf_t *b, uint32_t sample_count) +{ + u_char *pos; + + pos = ngx_rtmp_mp4_start_box(b, "saiz"); + + /* version & flag */ + ngx_rtmp_mp4_field_32(b, 0); + + /* defaut sample info size */ + ngx_rtmp_mp4_field_8(b, 8); + + /* sample count */ + ngx_rtmp_mp4_field_32(b, sample_count); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_mp4_write_saio(ngx_buf_t *b, u_char *moof_pos) +{ + u_char *pos; + + pos = ngx_rtmp_mp4_start_box(b, "saio"); + + /* version & flag */ + ngx_rtmp_mp4_field_32(b, 0); + + /* entry count */ + ngx_rtmp_mp4_field_32(b, 1); + + /* entry 0 offset */ + ngx_rtmp_mp4_field_32(b, 341); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_mp4_write_senc(ngx_buf_t *b, uint32_t sample_count, + ngx_rtmp_mp4_sample_t *samples) +{ + u_char *pos; + uint32_t i; + + pos = ngx_rtmp_mp4_start_box(b, "senc"); + + /* version & flag */ + ngx_rtmp_mp4_field_32(b, 0); + + /* sample count */ + ngx_rtmp_mp4_field_32(b, sample_count); + + for (i = 0; i < sample_count; i++, samples++) { + + /* IV per sample */ + ngx_rtmp_mp4_data(b, samples->iv, NGX_RTMP_AES_CTR_IV_SIZE); + + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, - ngx_uint_t sample_mask, u_char *moof_pos) + ngx_uint_t sample_mask, u_char *moof_pos, ngx_flag_t is_protected) { u_char *pos; @@ -1060,7 +1142,8 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, ngx_rtmp_mp4_write_tfhd(b); ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time); - ngx_rtmp_mp4_write_trun(b, sample_count, samples, sample_mask, moof_pos); + ngx_rtmp_mp4_write_trun(b, sample_count, samples, sample_mask, + moof_pos, is_protected); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1140,7 +1223,7 @@ ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, - ngx_uint_t sample_mask, uint32_t index) + ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected) { u_char *pos; @@ -1148,7 +1231,13 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, ngx_rtmp_mp4_write_mfhd(b, index); ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, samples, - sample_mask, pos); + sample_mask, pos, is_protected); + + if (is_protected) { + ngx_rtmp_mp4_write_saiz(b, sample_count); + ngx_rtmp_mp4_write_saio(b, pos); + ngx_rtmp_mp4_write_senc(b, sample_count, samples); + } ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 5f929d885..c56f21a0d 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -7,6 +7,7 @@ #include #include #include +#include "ngx_rtmp_aes_ctr.h" #define NGX_RTMP_MP4_SAMPLE_SIZE 0x01 @@ -44,7 +45,7 @@ ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_track_type_t ttype); ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, - ngx_uint_t sample_mask, uint32_t index); + ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected); ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, uint32_t earliest_pres_time, uint32_t latest_pres_time); From 0911590028445e4ebda4b28ce3bc0aa5383e8305 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 10 May 2018 19:40:06 +0000 Subject: [PATCH 060/113] Hope this is correct; saio offset to first iv in senc --- dash/ngx_rtmp_mp4.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 1c3b233ba..5b7eb8e61 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1085,6 +1085,7 @@ static ngx_int_t ngx_rtmp_mp4_write_saio(ngx_buf_t *b, u_char *moof_pos) { u_char *pos; + uint32_t offset; pos = ngx_rtmp_mp4_start_box(b, "saio"); @@ -1094,8 +1095,9 @@ ngx_rtmp_mp4_write_saio(ngx_buf_t *b, u_char *moof_pos) /* entry count */ ngx_rtmp_mp4_field_32(b, 1); - /* entry 0 offset */ - ngx_rtmp_mp4_field_32(b, 341); + /* entry 0 is offset to the first IV in senc box */ + offset = (pos - moof_pos) + 20 + 16; + ngx_rtmp_mp4_field_32(b, offset); ngx_rtmp_mp4_update_box_size(b, pos); From f3e2d568bbfade2db70b1a71fd657df578aad8d1 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 11 May 2018 09:37:00 +0000 Subject: [PATCH 061/113] Fix iv allocation and increment --- dash/ngx_rtmp_dash_module.c | 15 ++++++++------- dash/ngx_rtmp_mp4.h | 2 +- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 597638117..a00326a01 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -51,6 +51,7 @@ typedef struct { uint32_t earliest_pres_time; uint32_t latest_pres_time; unsigned is_protected:1; + u_char iv[NGX_RTMP_AES_CTR_IV_SIZE]; ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; } ngx_rtmp_dash_track_t; @@ -1769,12 +1770,12 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, ngx_rtmp_mp4_sample_t *smpl; static u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; - static u_char cenc_iv[NGX_RTMP_AES_CTR_IV_SIZE]; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; if (t->is_protected) { - ngx_cpymem(cenc_key, (u_char *)"0123456789abcdef", NGX_RTMP_AES_CTR_KEY_SIZE); - //ngx_cpymem(cenc_iv, (u_char *)"\xde\xad\xbe\xaf\xf0\x0d\xba\xad", NGX_RTMP_AES_CTR_IV_SIZE); + ngx_cpymem(cenc_key, + (u_char *)"\xde\xad\xbe\xaf\xf0\x0d\xba\xad\xde\xad\xbe\xaf\xf0\x0d\xba\xad", + NGX_RTMP_AES_CTR_KEY_SIZE); } p = buffer; @@ -1796,7 +1797,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count == 0) { t->earliest_pres_time = timestamp; if (t->is_protected) { - ngx_rtmp_aes_rand_iv(cenc_iv); + ngx_rtmp_aes_rand_iv(t->iv); } } @@ -1805,8 +1806,8 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { if (t->is_protected) { - ngx_rtmp_aes_ctr_encrypt(s, cenc_key, cenc_iv, buffer, size); - ngx_rtmp_aes_increment_iv(cenc_iv); + ngx_rtmp_aes_ctr_encrypt(s, cenc_key, t->iv, buffer, size); + ngx_rtmp_aes_increment_iv(t->iv); } if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { @@ -1831,7 +1832,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->is_protected) { smpl->is_protected = 1; - smpl->iv = cenc_iv; + ngx_memcpy(smpl->iv, t->iv, NGX_RTMP_AES_CTR_IV_SIZE); } if (t->sample_count > 0) { diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index c56f21a0d..fbcb32aca 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -23,7 +23,7 @@ typedef struct { uint32_t timestamp; unsigned key:1; unsigned is_protected:1; - u_char *iv; + u_char iv[NGX_RTMP_AES_CTR_IV_SIZE]; } ngx_rtmp_mp4_sample_t; From f05f65b4114f243b65d3bba187097c7e1e04232e Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 11 May 2018 16:44:54 +0000 Subject: [PATCH 062/113] Change key for test --- dash/ngx_rtmp_dash_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index a00326a01..22c4e0a77 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1774,7 +1774,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->is_protected) { ngx_cpymem(cenc_key, - (u_char *)"\xde\xad\xbe\xaf\xf0\x0d\xba\xad\xde\xad\xbe\xaf\xf0\x0d\xba\xad", + (u_char *)"\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d", NGX_RTMP_AES_CTR_KEY_SIZE); } From aafe3a444059aaa081e37fae0f8740aae35f0697 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 13 May 2018 10:40:31 +0000 Subject: [PATCH 063/113] Read key from file specified in dash_cenc_key configuration parameter --- dash/ngx_rtmp_dash_module.c | 41 +++++++++++++++++++++++++++++-------- 1 file changed, 32 insertions(+), 9 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 22c4e0a77..6112d40a0 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -51,6 +51,7 @@ typedef struct { uint32_t earliest_pres_time; uint32_t latest_pres_time; unsigned is_protected:1; + u_char key[NGX_RTMP_AES_CTR_KEY_SIZE]; u_char iv[NGX_RTMP_AES_CTR_IV_SIZE]; ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; } ngx_rtmp_dash_track_t; @@ -136,8 +137,9 @@ typedef struct { ngx_msec_t fraglen; ngx_msec_t playlen; ngx_flag_t nested; - ngx_flag_t repetition; ngx_flag_t cenc; + ngx_str_t cenc_key; + ngx_flag_t repetition; ngx_uint_t clock_compensation; // Try to compensate clock drift // between client and server (on client side) ngx_str_t clock_helper_uri; // Use uri to static file on HTTP server @@ -210,6 +212,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, cenc), NULL }, + { ngx_string("dash_cenc_key"), + 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_dash_app_conf_t, cenc_key), + NULL }, + { ngx_string("dash_clock_compensation"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -1313,6 +1322,26 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, t->opened = 1; if (dacf->cenc) { + ngx_fd_t fd; + ssize_t n; + + /* read key from key_path file */ + fd = ngx_open_file(dacf->cenc_key.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + + if (fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error reading key file"); + return NGX_ERROR; + } + + n = ngx_read_fd(fd, t->key, NGX_RTMP_AES_CTR_KEY_SIZE); + if (n != NGX_RTMP_AES_CTR_KEY_SIZE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error cenc key length is invalid"); + return NGX_ERROR; + } + ngx_close_file(fd); + t->is_protected = 1; } @@ -1769,15 +1798,8 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, size_t size, bsize; ngx_rtmp_mp4_sample_t *smpl; - static u_char cenc_key[NGX_RTMP_AES_CTR_KEY_SIZE]; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - if (t->is_protected) { - ngx_cpymem(cenc_key, - (u_char *)"\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d\xf0\x0d", - NGX_RTMP_AES_CTR_KEY_SIZE); - } - p = buffer; size = 0; @@ -1806,7 +1828,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { if (t->is_protected) { - ngx_rtmp_aes_ctr_encrypt(s, cenc_key, t->iv, buffer, size); + ngx_rtmp_aes_ctr_encrypt(s, t->key, t->iv, buffer, size); ngx_rtmp_aes_increment_iv(t->iv); } @@ -2573,6 +2595,7 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->nested, prev->nested, 0); ngx_conf_merge_value(conf->repetition, prev->repetition, 0); ngx_conf_merge_value(conf->cenc, prev->cenc, 0); + ngx_conf_merge_str_value(conf->cenc_key, prev->cenc_key, ""); ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); From e0b1293634098b110322fb517f705ca6859d3144 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 13 May 2018 12:10:05 +0000 Subject: [PATCH 064/113] Some debug to see everything is ok --- dash/ngx_rtmp_aes_ctr.c | 24 +++++++++++++----------- dash/ngx_rtmp_aes_ctr.h | 3 +-- dash/ngx_rtmp_dash_module.c | 2 +- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/dash/ngx_rtmp_aes_ctr.c b/dash/ngx_rtmp_aes_ctr.c index 70be3307e..97b45337d 100644 --- a/dash/ngx_rtmp_aes_ctr.c +++ b/dash/ngx_rtmp_aes_ctr.c @@ -10,15 +10,18 @@ void -print_counter(ngx_rtmp_session_t *s, uint8_t *c, size_t l) +debug_counter(ngx_rtmp_session_t *s, uint8_t *c, uint8_t *k, size_t l) { - u_char hex[AES_BLOCK_SIZE*2+1]; + u_char hexc[AES_BLOCK_SIZE*2+1]; + u_char hexk[AES_BLOCK_SIZE*2+1]; - ngx_hex_dump(hex, c, AES_BLOCK_SIZE); - hex[AES_BLOCK_SIZE*2] = '\0'; + ngx_hex_dump(hexc, c, AES_BLOCK_SIZE); + ngx_hex_dump(hexk, k, AES_BLOCK_SIZE); + hexc[AES_BLOCK_SIZE*2] = '\0'; + hexk[AES_BLOCK_SIZE*2] = '\0'; - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash aes_counter: %ui %s", l, hex); + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash aes_counter: %ui %s %s", l, hexc, hexk); } @@ -47,8 +50,7 @@ ngx_rtmp_aes_increment_iv(u_char* iv) ngx_int_t -ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, - const uint8_t *key, const uint8_t *iv, +ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len) { @@ -63,20 +65,20 @@ ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, cipher = EVP_CIPHER_CTX_new(); if (cipher == NULL) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash rtmp_aes_ctr_encrypt: evp_cipher_ctx failed"); return NGX_ERROR; } if (EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash rtmp_aes_ctr_encrypt: evp_encrypt_init failed"); return NGX_ERROR; } while (left > 0) { - print_counter(s, counter, left); + debug_counter(s, counter, key, left); EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; diff --git a/dash/ngx_rtmp_aes_ctr.h b/dash/ngx_rtmp_aes_ctr.h index 98a75dc0a..865634e7b 100644 --- a/dash/ngx_rtmp_aes_ctr.h +++ b/dash/ngx_rtmp_aes_ctr.h @@ -12,8 +12,7 @@ void ngx_rtmp_aes_increment_iv(u_char* iv); ngx_int_t -ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, - const uint8_t *key, const uint8_t *nonce, +ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len); #endif /* _NGX_RTMP_AES_CTR_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 6112d40a0..2558fdf97 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1336,7 +1336,7 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, n = ngx_read_fd(fd, t->key, NGX_RTMP_AES_CTR_KEY_SIZE); if (n != NGX_RTMP_AES_CTR_KEY_SIZE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc key length is invalid"); return NGX_ERROR; } From 97e7ba9b14ed764c719f2e5913023a33b6f41496 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 14 May 2018 16:28:09 +0000 Subject: [PATCH 065/113] Initial try to signaling encv --- dash/ngx_rtmp_dash_module.c | 24 ++++- dash/ngx_rtmp_mp4.c | 196 ++++++++++++++++++++++++++++++++++-- dash/ngx_rtmp_mp4.h | 4 +- 3 files changed, 210 insertions(+), 14 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 2558fdf97..77699d30b 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -23,7 +23,8 @@ static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf); static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child); -static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s); +static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, + ngx_flag_t is_protected); static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s); @@ -751,7 +752,11 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (ctx->id == 0) { - ngx_rtmp_dash_write_init_segments(s); + if (dacf->cenc) { + ngx_rtmp_dash_write_init_segments(s, 1); + } else { + ngx_rtmp_dash_write_init_segments(s, 0); + } } fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, @@ -1032,7 +1037,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) static ngx_int_t -ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) +ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, + ngx_flag_t is_protected) { ngx_fd_t fd; ngx_int_t rc; @@ -1067,7 +1073,11 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) b.pos = b.last = b.start; ngx_rtmp_mp4_write_ftyp(&b); - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK); + if (is_protected) { + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EVIDEO_TRACK); + } else { + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK); + } rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); if (rc == NGX_ERROR) { @@ -1093,7 +1103,11 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) b.pos = b.last = b.start; ngx_rtmp_mp4_write_ftyp(&b); - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK); + if (is_protected) { + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EAUDIO_TRACK); + } else { + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK); + } rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); if (rc == NGX_ERROR) { diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 5b7eb8e61..3bd003c67 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -316,20 +316,27 @@ ngx_rtmp_mp4_write_tkhd(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_16(b, ttype == NGX_RTMP_MP4_VIDEO_TRACK ? 0 : 0x0100); + if (ttype == NGX_RTMP_MP4_VIDEO_TRACK || + ttype == NGX_RTMP_MP4_EVIDEO_TRACK) { + ngx_rtmp_mp4_field_16(b, 0); + } else { + /* reserved */ + ngx_rtmp_mp4_field_16(b, 0x0100); + } /* reserved */ ngx_rtmp_mp4_field_16(b, 0); ngx_rtmp_mp4_write_matrix(b, 1, 0, 0, 1, 0, 0); - if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { + if (ttype == NGX_RTMP_MP4_VIDEO_TRACK || + ttype == NGX_RTMP_MP4_EVIDEO_TRACK) { ngx_rtmp_mp4_field_32(b, (uint32_t) codec_ctx->width << 16); ngx_rtmp_mp4_field_32(b, (uint32_t) codec_ctx->height << 16); } else { ngx_rtmp_mp4_field_32(b, 0); ngx_rtmp_mp4_field_32(b, 0); - } + } ngx_rtmp_mp4_update_box_size(b, pos); @@ -384,7 +391,8 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_track_type_t ttype) /* pre defined */ ngx_rtmp_mp4_field_32(b, 0); - if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { + if (ttype == NGX_RTMP_MP4_VIDEO_TRACK || + ttype == NGX_RTMP_MP4_EVIDEO_TRACK) { ngx_rtmp_mp4_box(b, "vide"); } else { ngx_rtmp_mp4_box(b, "soun"); @@ -395,7 +403,8 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_track_type_t ttype) ngx_rtmp_mp4_field_32(b, 0); ngx_rtmp_mp4_field_32(b, 0); - if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { + if (ttype == NGX_RTMP_MP4_VIDEO_TRACK || + ttype == NGX_RTMP_MP4_EVIDEO_TRACK) { /* video handler string, NULL-terminated */ ngx_rtmp_mp4_data(b, "VideoHandler", sizeof("VideoHandler")); } else { @@ -489,6 +498,108 @@ ngx_rtmp_mp4_write_dinf(ngx_buf_t *b) } +static ngx_int_t +ngx_rtmp_mp4_write_frma(ngx_buf_t *b) +{ + u_char *pos; + + pos = ngx_rtmp_mp4_start_box(b, "frma"); + + /* original_format */ + ngx_rtmp_mp4_box(b, "avc1"); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_mp4_write_schm(ngx_buf_t *b) +{ + u_char *pos; + + pos = ngx_rtmp_mp4_start_box(b, "schm"); + + /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0); + + /* scheme_type */ + ngx_rtmp_mp4_box(b, "cenc"); + + /* scheme_version */ + ngx_rtmp_mp4_field_32(b, 65536); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_mp4_write_tenc(ngx_buf_t *b) +{ + u_char *pos; + + pos = ngx_rtmp_mp4_start_box(b, "tenc"); + + /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0); + + /* reserved */ + ngx_rtmp_mp4_field_8(b, 0); + ngx_rtmp_mp4_field_8(b, 0); + + /* default isProtected */ + ngx_rtmp_mp4_field_8(b, 1); + + /* default per_sample_iv_size */ + ngx_rtmp_mp4_field_8(b, 8); + + /* default KID */ + ngx_rtmp_mp4_field_32(b, 0x00480000); + ngx_rtmp_mp4_field_32(b, 0x00480000); + ngx_rtmp_mp4_field_32(b, 0x00480000); + ngx_rtmp_mp4_field_32(b, 0x00480000); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_mp4_write_schi(ngx_buf_t *b) +{ + u_char *pos; + + pos = ngx_rtmp_mp4_start_box(b, "schi"); + + ngx_rtmp_mp4_write_tenc(b); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_mp4_write_sinf(ngx_buf_t *b) +{ + u_char *pos; + + pos = ngx_rtmp_mp4_start_box(b, "sinf"); + + ngx_rtmp_mp4_write_frma(b); + ngx_rtmp_mp4_write_schm(b); + ngx_rtmp_mp4_write_schi(b); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b) { @@ -597,6 +708,70 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b) } +static ngx_int_t +ngx_rtmp_mp4_write_svideo(ngx_rtmp_session_t *s, ngx_buf_t *b) +{ + u_char *pos; + ngx_rtmp_codec_ctx_t *codec_ctx; + + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + pos = ngx_rtmp_mp4_start_box(b, "encv"); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_16(b, 0); + + /* data reference index */ + ngx_rtmp_mp4_field_16(b, 1); + + /* codec stream version & revision */ + ngx_rtmp_mp4_field_16(b, 0); + ngx_rtmp_mp4_field_16(b, 0); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + + /* width & height */ + ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->width); + ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->height); + + /* horizontal & vertical resolutions 72 dpi */ + ngx_rtmp_mp4_field_32(b, 0x00480000); + ngx_rtmp_mp4_field_32(b, 0x00480000); + + /* data size */ + ngx_rtmp_mp4_field_32(b, 0); + + /* frame count */ + ngx_rtmp_mp4_field_16(b, 1); + + /* compressor name */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_16(b, 0x18); + ngx_rtmp_mp4_field_16(b, 0xffff); + + ngx_rtmp_mp4_write_avcc(s, b); + + ngx_rtmp_mp4_write_sinf(b); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) { @@ -741,9 +916,13 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, /* entry count */ ngx_rtmp_mp4_field_32(b, 1); - if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { + if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { ngx_rtmp_mp4_write_video(s, b); - } else { + } else if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK){ + ngx_rtmp_mp4_write_svideo(s, b); + } else if (ttype == NGX_RTMP_MP4_AUDIO_TRACK){ + ngx_rtmp_mp4_write_audio(s, b); + } else if (ttype == NGX_RTMP_MP4_EAUDIO_TRACK){ ngx_rtmp_mp4_write_audio(s, b); } @@ -846,7 +1025,8 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, pos = ngx_rtmp_mp4_start_box(b, "minf"); - if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { + if (ttype == NGX_RTMP_MP4_VIDEO_TRACK || + ttype == NGX_RTMP_MP4_EVIDEO_TRACK) { ngx_rtmp_mp4_write_vmhd(b); } else { ngx_rtmp_mp4_write_smhd(b); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index fbcb32aca..aeb3ea51c 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -35,7 +35,9 @@ typedef enum { typedef enum { NGX_RTMP_MP4_VIDEO_TRACK, - NGX_RTMP_MP4_AUDIO_TRACK + NGX_RTMP_MP4_AUDIO_TRACK, + NGX_RTMP_MP4_EVIDEO_TRACK, + NGX_RTMP_MP4_EAUDIO_TRACK } ngx_rtmp_mp4_track_type_t; From 4c0c874bab3fce22b273e99502490e587eb1c5dd Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 15 May 2018 09:59:46 +0000 Subject: [PATCH 066/113] Add encrypted audio sig --- dash/ngx_rtmp_mp4.c | 69 +++++++++++++++++++++++++++++++++++++++------ 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 3bd003c67..50f8968d3 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -499,14 +499,14 @@ ngx_rtmp_mp4_write_dinf(ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_frma(ngx_buf_t *b) +ngx_rtmp_mp4_write_frma(ngx_buf_t *b, const char format[4]) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "frma"); /* original_format */ - ngx_rtmp_mp4_box(b, "avc1"); + ngx_rtmp_mp4_box(b, format); ngx_rtmp_mp4_update_box_size(b, pos); @@ -584,13 +584,13 @@ ngx_rtmp_mp4_write_schi(ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_sinf(ngx_buf_t *b) +ngx_rtmp_mp4_write_sinf(ngx_buf_t *b, const char format[4]) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "sinf"); - ngx_rtmp_mp4_write_frma(b); + ngx_rtmp_mp4_write_frma(b, format); ngx_rtmp_mp4_write_schm(b); ngx_rtmp_mp4_write_schi(b); @@ -709,7 +709,7 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_svideo(ngx_rtmp_session_t *s, ngx_buf_t *b) +ngx_rtmp_mp4_write_evideo(ngx_rtmp_session_t *s, ngx_buf_t *b) { u_char *pos; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -764,7 +764,7 @@ ngx_rtmp_mp4_write_svideo(ngx_rtmp_session_t *s, ngx_buf_t *b) ngx_rtmp_mp4_write_avcc(s, b); - ngx_rtmp_mp4_write_sinf(b); + ngx_rtmp_mp4_write_sinf(b, "avc1"); ngx_rtmp_mp4_update_box_size(b, pos); @@ -902,6 +902,59 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b) } +static ngx_int_t +ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, ngx_buf_t *b) +{ + u_char *pos; + ngx_rtmp_codec_ctx_t *codec_ctx; + + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + pos = ngx_rtmp_mp4_start_box(b, "enca"); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_16(b, 0); + + /* data reference index */ + ngx_rtmp_mp4_field_16(b, 1); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + + /* channel count */ + ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->audio_channels); + + /* sample size */ + ngx_rtmp_mp4_field_16(b, (uint16_t) (codec_ctx->sample_size * 8)); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + + /* time scale */ + ngx_rtmp_mp4_field_16(b, 1000); + + /* sample rate */ + ngx_rtmp_mp4_field_16(b, (uint16_t) codec_ctx->sample_rate); + + ngx_rtmp_mp4_write_esds(s, b); +#if 0 + /* tag size*/ + ngx_rtmp_mp4_field_32(b, 8); + + /* null tag */ + ngx_rtmp_mp4_field_32(b, 0); +#endif + + ngx_rtmp_mp4_write_sinf(b, "mp4a"); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_track_type_t ttype) @@ -919,11 +972,11 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { ngx_rtmp_mp4_write_video(s, b); } else if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK){ - ngx_rtmp_mp4_write_svideo(s, b); + ngx_rtmp_mp4_write_evideo(s, b); } else if (ttype == NGX_RTMP_MP4_AUDIO_TRACK){ ngx_rtmp_mp4_write_audio(s, b); } else if (ttype == NGX_RTMP_MP4_EAUDIO_TRACK){ - ngx_rtmp_mp4_write_audio(s, b); + ngx_rtmp_mp4_write_eaudio(s, b); } ngx_rtmp_mp4_update_box_size(b, pos); From ab0f53eea31022e447cf71e7a33d89be74505af5 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 15 May 2018 15:10:21 +0000 Subject: [PATCH 067/113] Well not in the good box location --- dash/ngx_rtmp_mp4.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 50f8968d3..cd01103fd 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1380,6 +1380,12 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, ngx_rtmp_mp4_write_trun(b, sample_count, samples, sample_mask, moof_pos, is_protected); + if (is_protected) { + ngx_rtmp_mp4_write_saiz(b, sample_count); + ngx_rtmp_mp4_write_saio(b, pos); + ngx_rtmp_mp4_write_senc(b, sample_count, samples); + } + ngx_rtmp_mp4_update_box_size(b, pos); return NGX_OK; @@ -1468,12 +1474,6 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, samples, sample_mask, pos, is_protected); - if (is_protected) { - ngx_rtmp_mp4_write_saiz(b, sample_count); - ngx_rtmp_mp4_write_saio(b, pos); - ngx_rtmp_mp4_write_senc(b, sample_count, samples); - } - ngx_rtmp_mp4_update_box_size(b, pos); return NGX_OK; From 7e2dd3046d64dbf4ccd0dca3e2a75b694535eee9 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 15 May 2018 15:51:35 +0000 Subject: [PATCH 068/113] Grmbl I known this will happen ;/ --- dash/ngx_rtmp_dash_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 77699d30b..0ebd3b3a2 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1843,7 +1843,6 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->is_protected) { ngx_rtmp_aes_ctr_encrypt(s, t->key, t->iv, buffer, size); - ngx_rtmp_aes_increment_iv(t->iv); } if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { @@ -1869,6 +1868,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->is_protected) { smpl->is_protected = 1; ngx_memcpy(smpl->iv, t->iv, NGX_RTMP_AES_CTR_IV_SIZE); + ngx_rtmp_aes_increment_iv(t->iv); } if (t->sample_count > 0) { From c9f7f6d999bb6479cd923c139205c6787e861bba Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 16 May 2018 13:58:32 +0000 Subject: [PATCH 069/113] renaming aes_ctr to cenc --- TODO | 7 ++-- config | 4 +- dash/ngx_rtmp_aes_ctr.h | 18 --------- dash/{ngx_rtmp_aes_ctr.c => ngx_rtmp_cenc.c} | 23 +++++------ dash/ngx_rtmp_cenc.h | 18 +++++++++ dash/ngx_rtmp_dash_module.c | 40 ++++++++++---------- dash/ngx_rtmp_mp4.c | 4 +- dash/ngx_rtmp_mp4.h | 4 +- 8 files changed, 60 insertions(+), 58 deletions(-) delete mode 100644 dash/ngx_rtmp_aes_ctr.h rename dash/{ngx_rtmp_aes_ctr.c => ngx_rtmp_cenc.c} (70%) create mode 100644 dash/ngx_rtmp_cenc.h diff --git a/TODO b/TODO index a8d504647..50cefab05 100644 --- a/TODO +++ b/TODO @@ -1,3 +1,4 @@ -- rewritte the variant code for dash, use separate file, and concat them in the manifest -- add an option for inbant event handling, allow the choice of using onCuePoint/onCuePoint_scte35 -- add support for common encryption, early stage in aes_ctr.c +- need doc +- need re-upstream +- test common encryption code +- rewritte the variant code for dash, using memory ? diff --git a/config b/config index baf15fb88..32f6d551e 100644 --- a/config +++ b/config @@ -43,7 +43,7 @@ RTMP_DEPS=" \ $ngx_addon_dir/ngx_rtmp_proxy_protocol.h \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.h \ $ngx_addon_dir/dash/ngx_rtmp_mp4.h \ - $ngx_addon_dir/dash/ngx_rtmp_aes_ctr.h \ + $ngx_addon_dir/dash/ngx_rtmp_cenc.h \ $ngx_addon_dir/dash/ngx_rtmp_dash_templates.h \ " RTMP_CORE_SRCS=" \ @@ -80,7 +80,7 @@ RTMP_CORE_SRCS=" \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.c \ $ngx_addon_dir/hls/ngx_rtmp_mpegts_crc.c \ $ngx_addon_dir/dash/ngx_rtmp_mp4.c \ - $ngx_addon_dir/dash/ngx_rtmp_aes_ctr.c \ + $ngx_addon_dir/dash/ngx_rtmp_cenc.c \ " RTMP_HTTP_SRCS=" \ $ngx_addon_dir/ngx_rtmp_stat_module.c \ diff --git a/dash/ngx_rtmp_aes_ctr.h b/dash/ngx_rtmp_aes_ctr.h deleted file mode 100644 index 865634e7b..000000000 --- a/dash/ngx_rtmp_aes_ctr.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef _NGX_RTMP_AES_CTR_H_INCLUDED_ -#define _NGX_RTMP_AES_CTR_H_INCLUDED_ - -#define NGX_RTMP_AES_CTR_IV_SIZE (8) -#define NGX_RTMP_AES_CTR_KEY_SIZE (16) - - -ngx_int_t -ngx_rtmp_aes_rand_iv(u_char* iv); - -void -ngx_rtmp_aes_increment_iv(u_char* iv); - -ngx_int_t -ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, - uint8_t *data, size_t data_len); - -#endif /* _NGX_RTMP_AES_CTR_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_aes_ctr.c b/dash/ngx_rtmp_cenc.c similarity index 70% rename from dash/ngx_rtmp_aes_ctr.c rename to dash/ngx_rtmp_cenc.c index 97b45337d..d07a1e5e9 100644 --- a/dash/ngx_rtmp_aes_ctr.c +++ b/dash/ngx_rtmp_cenc.c @@ -6,7 +6,7 @@ #include #include #include -#include "ngx_rtmp_aes_ctr.h" +#include "ngx_rtmp_cenc.h" void @@ -21,14 +21,14 @@ debug_counter(ngx_rtmp_session_t *s, uint8_t *c, uint8_t *k, size_t l) hexk[AES_BLOCK_SIZE*2] = '\0'; ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash aes_counter: %ui %s %s", l, hexc, hexk); + "dash cenc_counter: %ui %s %s", l, hexc, hexk); } ngx_int_t -ngx_rtmp_aes_rand_iv(u_char* iv) +ngx_rtmp_cenc_rand_iv(u_char* iv) { - if(RAND_bytes(iv, NGX_RTMP_AES_CTR_IV_SIZE) != 1) { + if(RAND_bytes(iv, NGX_RTMP_CENC_IV_SIZE) != 1) { return NGX_ERROR; } @@ -37,11 +37,11 @@ ngx_rtmp_aes_rand_iv(u_char* iv) void -ngx_rtmp_aes_increment_iv(u_char* iv) +ngx_rtmp_cenc_increment_iv(u_char* iv) { int i; - for (i = NGX_RTMP_AES_CTR_IV_SIZE - 1; i >= 0; i--) { + for (i = NGX_RTMP_CENC_IV_SIZE - 1; i >= 0; i--) { iv[i]++; if (iv[i]) break; @@ -50,9 +50,10 @@ ngx_rtmp_aes_increment_iv(u_char* iv) ngx_int_t -ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, +ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len) { + /* aes-ctr implementation */ EVP_CIPHER_CTX* cipher; size_t j, len, left = data_len; @@ -60,19 +61,19 @@ ngx_rtmp_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *pos = data; uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; - ngx_memset(counter + NGX_RTMP_AES_CTR_IV_SIZE, 0, NGX_RTMP_AES_CTR_IV_SIZE); - ngx_memcpy(counter, iv, NGX_RTMP_AES_CTR_IV_SIZE); + ngx_memset(counter + NGX_RTMP_CENC_IV_SIZE, 0, NGX_RTMP_CENC_IV_SIZE); + ngx_memcpy(counter, iv, NGX_RTMP_CENC_IV_SIZE); cipher = EVP_CIPHER_CTX_new(); if (cipher == NULL) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash rtmp_aes_ctr_encrypt: evp_cipher_ctx failed"); + "dash rtmp_cenc_encrypt: evp_cipher_ctx failed"); return NGX_ERROR; } if (EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash rtmp_aes_ctr_encrypt: evp_encrypt_init failed"); + "dash rtmp_cenc_encrypt: evp_encrypt_init failed"); return NGX_ERROR; } diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h new file mode 100644 index 000000000..0211131bc --- /dev/null +++ b/dash/ngx_rtmp_cenc.h @@ -0,0 +1,18 @@ +#ifndef _NGX_RTMP_CENC_H_INCLUDED_ +#define _NGX_RTMP_CENC_H_INCLUDED_ + +#define NGX_RTMP_CENC_IV_SIZE (8) +#define NGX_RTMP_CENC_KEY_SIZE (16) + + +ngx_int_t +ngx_rtmp_cenc_rand_iv(u_char* iv); + +void +ngx_rtmp_cenc_increment_iv(u_char* iv); + +ngx_int_t +ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, + uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len); + +#endif /* _NGX_RTMP_CENC_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 0ebd3b3a2..a991b4590 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -7,7 +7,7 @@ #include "ngx_rtmp_live_module.h" #include "ngx_rtmp_mp4.h" #include "ngx_rtmp_dash_templates.h" -#include "ngx_rtmp_aes_ctr.h" +#include "ngx_rtmp_cenc.h" static ngx_rtmp_publish_pt next_publish; @@ -52,8 +52,8 @@ typedef struct { uint32_t earliest_pres_time; uint32_t latest_pres_time; unsigned is_protected:1; - u_char key[NGX_RTMP_AES_CTR_KEY_SIZE]; - u_char iv[NGX_RTMP_AES_CTR_IV_SIZE]; + u_char key[NGX_RTMP_CENC_KEY_SIZE]; + u_char iv[NGX_RTMP_CENC_IV_SIZE]; ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; } ngx_rtmp_dash_track_t; @@ -140,6 +140,7 @@ typedef struct { ngx_flag_t nested; ngx_flag_t cenc; ngx_str_t cenc_key; + ngx_str_t cenc_kid; ngx_flag_t repetition; ngx_uint_t clock_compensation; // Try to compensate clock drift // between client and server (on client side) @@ -220,6 +221,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, cenc_key), NULL }, + { ngx_string("dash_cenc_kid"), + 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_dash_app_conf_t, cenc_kid), + NULL }, + { ngx_string("dash_clock_compensation"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -1336,25 +1344,16 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, t->opened = 1; if (dacf->cenc) { - ngx_fd_t fd; - ssize_t n; - /* read key from key_path file */ - fd = ngx_open_file(dacf->cenc_key.data, NGX_FILE_RDONLY, NGX_FILE_OPEN, 0); + dacf->cenc_key.data - if (fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error reading key file"); - return NGX_ERROR; - } - n = ngx_read_fd(fd, t->key, NGX_RTMP_AES_CTR_KEY_SIZE); - if (n != NGX_RTMP_AES_CTR_KEY_SIZE) { + n = ngx_read_fd(fd, t->key, NGX_RTMP_CENC_KEY_SIZE); + if ( ngx_aes== ) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: error cenc key length is invalid"); + "dash: error cenc key is invalid"); return NGX_ERROR; } - ngx_close_file(fd); t->is_protected = 1; } @@ -1833,7 +1832,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count == 0) { t->earliest_pres_time = timestamp; if (t->is_protected) { - ngx_rtmp_aes_rand_iv(t->iv); + ngx_rtmp_cenc_rand_iv(t->iv); } } @@ -1842,7 +1841,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { if (t->is_protected) { - ngx_rtmp_aes_ctr_encrypt(s, t->key, t->iv, buffer, size); + ngx_rtmp_cenc_encrypt(s, t->key, t->iv, buffer, size); } if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { @@ -1867,8 +1866,8 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->is_protected) { smpl->is_protected = 1; - ngx_memcpy(smpl->iv, t->iv, NGX_RTMP_AES_CTR_IV_SIZE); - ngx_rtmp_aes_increment_iv(t->iv); + ngx_memcpy(smpl->iv, t->iv, NGX_RTMP_CENC_IV_SIZE); + ngx_rtmp_cenc_increment_iv(t->iv); } if (t->sample_count > 0) { @@ -2610,6 +2609,7 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->repetition, prev->repetition, 0); ngx_conf_merge_value(conf->cenc, prev->cenc, 0); ngx_conf_merge_str_value(conf->cenc_key, prev->cenc_key, ""); + ngx_conf_merge_str_value(conf->cenc_kid, prev->cenc_kid, ""); ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index cd01103fd..640360e6b 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1257,7 +1257,7 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, * saiz(17) saio(20) senc(16 + sc*8) * to the data offset */ offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4); - offset += 17 + 20 + 16 + (sample_count * NGX_RTMP_AES_CTR_IV_SIZE) + 8; + offset += 17 + 20 + 16 + (sample_count * NGX_RTMP_CENC_IV_SIZE) + 8; } else { offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8; @@ -1356,7 +1356,7 @@ ngx_rtmp_mp4_write_senc(ngx_buf_t *b, uint32_t sample_count, for (i = 0; i < sample_count; i++, samples++) { /* IV per sample */ - ngx_rtmp_mp4_data(b, samples->iv, NGX_RTMP_AES_CTR_IV_SIZE); + ngx_rtmp_mp4_data(b, samples->iv, NGX_RTMP_CENC_IV_SIZE); } diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index aeb3ea51c..c474071ed 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -7,7 +7,7 @@ #include #include #include -#include "ngx_rtmp_aes_ctr.h" +#include "ngx_rtmp_cenc.h" #define NGX_RTMP_MP4_SAMPLE_SIZE 0x01 @@ -23,7 +23,7 @@ typedef struct { uint32_t timestamp; unsigned key:1; unsigned is_protected:1; - u_char iv[NGX_RTMP_AES_CTR_IV_SIZE]; + u_char iv[NGX_RTMP_CENC_IV_SIZE]; } ngx_rtmp_mp4_sample_t; From eb9f3af7ba29e62e65425811a7a8c6ba09200d20 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 16 May 2018 19:04:34 +0000 Subject: [PATCH 070/113] Good old fashioned c --- dash/ngx_rtmp_cenc.c | 228 ++++++++++++++++++++---------------- dash/ngx_rtmp_cenc.h | 2 + dash/ngx_rtmp_dash_module.c | 13 +- 3 files changed, 135 insertions(+), 108 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index d07a1e5e9..2f9cf75eb 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -1,103 +1,125 @@ - - -#include -#include -#include -#include -#include -#include -#include "ngx_rtmp_cenc.h" - - -void -debug_counter(ngx_rtmp_session_t *s, uint8_t *c, uint8_t *k, size_t l) -{ - u_char hexc[AES_BLOCK_SIZE*2+1]; - u_char hexk[AES_BLOCK_SIZE*2+1]; - - ngx_hex_dump(hexc, c, AES_BLOCK_SIZE); - ngx_hex_dump(hexk, k, AES_BLOCK_SIZE); - hexc[AES_BLOCK_SIZE*2] = '\0'; - hexk[AES_BLOCK_SIZE*2] = '\0'; - - ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash cenc_counter: %ui %s %s", l, hexc, hexk); -} - - -ngx_int_t -ngx_rtmp_cenc_rand_iv(u_char* iv) -{ - if(RAND_bytes(iv, NGX_RTMP_CENC_IV_SIZE) != 1) { - return NGX_ERROR; - } - - return NGX_OK; -} - - -void -ngx_rtmp_cenc_increment_iv(u_char* iv) -{ - int i; - - for (i = NGX_RTMP_CENC_IV_SIZE - 1; i >= 0; i--) { - iv[i]++; - if (iv[i]) - break; - } -} - - -ngx_int_t -ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, - uint8_t *data, size_t data_len) -{ - /* aes-ctr implementation */ - - EVP_CIPHER_CTX* cipher; - size_t j, len, left = data_len; - int i, w; - uint8_t *pos = data; - uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; - - ngx_memset(counter + NGX_RTMP_CENC_IV_SIZE, 0, NGX_RTMP_CENC_IV_SIZE); - ngx_memcpy(counter, iv, NGX_RTMP_CENC_IV_SIZE); - - cipher = EVP_CIPHER_CTX_new(); - if (cipher == NULL) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash rtmp_cenc_encrypt: evp_cipher_ctx failed"); - return NGX_ERROR; - } - - if (EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash rtmp_cenc_encrypt: evp_encrypt_init failed"); - return NGX_ERROR; - } - - while (left > 0) { - - debug_counter(s, counter, key, left); - EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); - - len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; - for (j = 0; j < len; j++) - pos[j] ^= buf[j]; - pos += len; - left -= len; - - for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { - counter[i]++; - if (counter[i]) - break; - } - } - - EVP_CIPHER_CTX_free(cipher); - - return NGX_OK; -} - - + + +#include +#include +#include +#include +#include +#include +#include "ngx_rtmp_cenc.h" + + +void +debug_counter(ngx_rtmp_session_t *s, uint8_t *c, uint8_t *k, size_t l) +{ + u_char hexc[AES_BLOCK_SIZE*2+1]; + u_char hexk[AES_BLOCK_SIZE*2+1]; + + ngx_hex_dump(hexc, c, AES_BLOCK_SIZE); + ngx_hex_dump(hexk, k, AES_BLOCK_SIZE); + hexc[AES_BLOCK_SIZE*2] = '\0'; + hexk[AES_BLOCK_SIZE*2] = '\0'; + + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash cenc_counter: %ui %s %s", l, hexc, hexk); +} + + +ngx_int_t +ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst) +{ + u_char l, h; + size_t i; + + if (src.len != NGX_RTMP_CENC_KEY_SIZE*2) { + return NGX_ERROR; + } + + for (i = 0; i < NGX_RTMP_CENC_KEY_SIZE; i++) { + l = tolower(src.data[i*2]); + h = tolower(src.data[i*2+1]); + l = l >= 'a' ? l - 'a' + 10 : l - '0'; + h = h >= 'a' ? h - 'a' + 10 : h - '0'; + dst[i] = (l <<= 4) | h; + } + + return NGX_OK; +} + + +ngx_int_t +ngx_rtmp_cenc_rand_iv(u_char* iv) +{ + if(RAND_bytes(iv, NGX_RTMP_CENC_IV_SIZE) != 1) { + return NGX_ERROR; + } + + return NGX_OK; +} + + +void +ngx_rtmp_cenc_increment_iv(u_char* iv) +{ + int i; + + for (i = NGX_RTMP_CENC_IV_SIZE - 1; i >= 0; i--) { + iv[i]++; + if (iv[i]) + break; + } +} + + +ngx_int_t +ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, + uint8_t *data, size_t data_len) +{ + /* aes-ctr implementation */ + + EVP_CIPHER_CTX* cipher; + size_t j, len, left = data_len; + int i, w; + uint8_t *pos = data; + uint8_t counter[AES_BLOCK_SIZE], buf[AES_BLOCK_SIZE]; + + ngx_memset(counter + NGX_RTMP_CENC_IV_SIZE, 0, NGX_RTMP_CENC_IV_SIZE); + ngx_memcpy(counter, iv, NGX_RTMP_CENC_IV_SIZE); + + cipher = EVP_CIPHER_CTX_new(); + if (cipher == NULL) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash rtmp_cenc_encrypt: evp_cipher_ctx failed"); + return NGX_ERROR; + } + + if (EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash rtmp_cenc_encrypt: evp_encrypt_init failed"); + return NGX_ERROR; + } + + while (left > 0) { + + debug_counter(s, counter, key, left); + EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); + + len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; + for (j = 0; j < len; j++) + pos[j] ^= buf[j]; + pos += len; + left -= len; + + for (i = AES_BLOCK_SIZE - 1; i >= 0; i--) { + counter[i]++; + if (counter[i]) + break; + } + } + + EVP_CIPHER_CTX_free(cipher); + + return NGX_OK; +} + + diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index 0211131bc..afbf3ebe7 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -4,6 +4,8 @@ #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) +ngx_int_t +ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); ngx_int_t ngx_rtmp_cenc_rand_iv(u_char* iv); diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index a991b4590..a16b9dd44 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -53,6 +53,7 @@ typedef struct { uint32_t latest_pres_time; unsigned is_protected:1; u_char key[NGX_RTMP_CENC_KEY_SIZE]; + u_char kid[NGX_RTMP_CENC_KEY_SIZE]; u_char iv[NGX_RTMP_CENC_IV_SIZE]; ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; } ngx_rtmp_dash_track_t; @@ -1345,16 +1346,18 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, if (dacf->cenc) { - dacf->cenc_key.data - - - n = ngx_read_fd(fd, t->key, NGX_RTMP_CENC_KEY_SIZE); - if ( ngx_aes== ) { + if (ngx_rtmp_cenc_read_hex(dacf->cenc_key, t->key) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc key is invalid"); return NGX_ERROR; } + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, t->kid) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: error cenc kid is invalid"); + return NGX_ERROR; + } + t->is_protected = 1; } From c5b383340f93d1fee03cedf9a93ef7ff003e521d Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 17 May 2018 11:51:59 +0000 Subject: [PATCH 071/113] Need a struct to pass kid/pssh information to mp4 init --- dash/ngx_rtmp_cenc.c | 2 +- dash/ngx_rtmp_dash_module.c | 51 ++++++++++++++++------------------- dash/ngx_rtmp_mp4.c | 54 ++++++++++++++++++------------------- dash/ngx_rtmp_mp4.h | 2 +- 4 files changed, 52 insertions(+), 57 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 2f9cf75eb..dd1ef6a0d 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -40,7 +40,7 @@ ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst) h = tolower(src.data[i*2+1]); l = l >= 'a' ? l - 'a' + 10 : l - '0'; h = h >= 'a' ? h - 'a' + 10 : h - '0'; - dst[i] = (l <<= 4) | h; + dst[i] = (l << 4) | h; } return NGX_OK; diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index a16b9dd44..ba3d3aca3 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -23,8 +23,7 @@ static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf); static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child); -static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, - ngx_flag_t is_protected); +static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s); static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s); @@ -53,7 +52,6 @@ typedef struct { uint32_t latest_pres_time; unsigned is_protected:1; u_char key[NGX_RTMP_CENC_KEY_SIZE]; - u_char kid[NGX_RTMP_CENC_KEY_SIZE]; u_char iv[NGX_RTMP_CENC_IV_SIZE]; ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; } ngx_rtmp_dash_track_t; @@ -761,11 +759,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (ctx->id == 0) { - if (dacf->cenc) { - ngx_rtmp_dash_write_init_segments(s, 1); - } else { - ngx_rtmp_dash_write_init_segments(s, 0); - } + ngx_rtmp_dash_write_init_segments(s); } fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, @@ -1046,21 +1040,23 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) static ngx_int_t -ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, - ngx_flag_t is_protected) +ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) { - ngx_fd_t fd; - ngx_int_t rc; - ngx_buf_t b; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_fd_t fd; + ngx_int_t rc; + ngx_buf_t b; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_app_conf_t *dacf; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + static u_char kid[NGX_RTMP_CENC_KEY_SIZE]; + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); - if (ctx == NULL || codec_ctx == NULL) { + if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { return NGX_ERROR; } @@ -1082,10 +1078,15 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, b.pos = b.last = b.start; ngx_rtmp_mp4_write_ftyp(&b); - if (is_protected) { - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EVIDEO_TRACK); + if (dacf->cenc) { + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: error cenc kid is invalid"); + return NGX_ERROR; + } + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EVIDEO_TRACK, kid); } else { - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK); + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK, NULL); } rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); @@ -1112,10 +1113,10 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, b.pos = b.last = b.start; ngx_rtmp_mp4_write_ftyp(&b); - if (is_protected) { - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EAUDIO_TRACK); + if (dacf->cenc) { + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EAUDIO_TRACK, kid); } else { - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK); + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK, NULL); } rc = ngx_write_fd(fd, b.start, (size_t) (b.last - b.start)); @@ -1352,12 +1353,6 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, return NGX_ERROR; } - if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, t->kid) == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: error cenc kid is invalid"); - return NGX_ERROR; - } - t->is_protected = 1; } diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 640360e6b..605f83587 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -537,7 +537,7 @@ ngx_rtmp_mp4_write_schm(ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_tenc(ngx_buf_t *b) +ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, u_char *kid) { u_char *pos; @@ -554,13 +554,10 @@ ngx_rtmp_mp4_write_tenc(ngx_buf_t *b) ngx_rtmp_mp4_field_8(b, 1); /* default per_sample_iv_size */ - ngx_rtmp_mp4_field_8(b, 8); + ngx_rtmp_mp4_field_8(b, NGX_RTMP_CENC_IV_SIZE); /* default KID */ - ngx_rtmp_mp4_field_32(b, 0x00480000); - ngx_rtmp_mp4_field_32(b, 0x00480000); - ngx_rtmp_mp4_field_32(b, 0x00480000); - ngx_rtmp_mp4_field_32(b, 0x00480000); + ngx_rtmp_mp4_data(b, kid, NGX_RTMP_CENC_KEY_SIZE); ngx_rtmp_mp4_update_box_size(b, pos); @@ -569,13 +566,13 @@ ngx_rtmp_mp4_write_tenc(ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_schi(ngx_buf_t *b) +ngx_rtmp_mp4_write_schi(ngx_buf_t *b, u_char *kid) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "schi"); - ngx_rtmp_mp4_write_tenc(b); + ngx_rtmp_mp4_write_tenc(b, kid); ngx_rtmp_mp4_update_box_size(b, pos); @@ -584,7 +581,8 @@ ngx_rtmp_mp4_write_schi(ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_sinf(ngx_buf_t *b, const char format[4]) +ngx_rtmp_mp4_write_sinf(ngx_buf_t *b, + const char format[4], u_char *kid) { u_char *pos; @@ -592,7 +590,7 @@ ngx_rtmp_mp4_write_sinf(ngx_buf_t *b, const char format[4]) ngx_rtmp_mp4_write_frma(b, format); ngx_rtmp_mp4_write_schm(b); - ngx_rtmp_mp4_write_schi(b); + ngx_rtmp_mp4_write_schi(b, kid); ngx_rtmp_mp4_update_box_size(b, pos); @@ -709,7 +707,8 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_evideo(ngx_rtmp_session_t *s, ngx_buf_t *b) +ngx_rtmp_mp4_write_evideo(ngx_rtmp_session_t *s, + ngx_buf_t *b, u_char *kid) { u_char *pos; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -764,7 +763,7 @@ ngx_rtmp_mp4_write_evideo(ngx_rtmp_session_t *s, ngx_buf_t *b) ngx_rtmp_mp4_write_avcc(s, b); - ngx_rtmp_mp4_write_sinf(b, "avc1"); + ngx_rtmp_mp4_write_sinf(b, "avc1", kid); ngx_rtmp_mp4_update_box_size(b, pos); @@ -903,7 +902,8 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, ngx_buf_t *b) +ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, + ngx_buf_t *b, u_char *kid) { u_char *pos; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -947,7 +947,7 @@ ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, ngx_buf_t *b) ngx_rtmp_mp4_field_32(b, 0); #endif - ngx_rtmp_mp4_write_sinf(b, "mp4a"); + ngx_rtmp_mp4_write_sinf(b, "mp4a", kid); ngx_rtmp_mp4_update_box_size(b, pos); @@ -957,7 +957,7 @@ ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype) + ngx_rtmp_mp4_track_type_t ttype, u_char *kid) { u_char *pos; @@ -972,11 +972,11 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { ngx_rtmp_mp4_write_video(s, b); } else if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK){ - ngx_rtmp_mp4_write_evideo(s, b); + ngx_rtmp_mp4_write_evideo(s, b, kid); } else if (ttype == NGX_RTMP_MP4_AUDIO_TRACK){ ngx_rtmp_mp4_write_audio(s, b); } else if (ttype == NGX_RTMP_MP4_EAUDIO_TRACK){ - ngx_rtmp_mp4_write_eaudio(s, b); + ngx_rtmp_mp4_write_eaudio(s, b, kid); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -1052,13 +1052,13 @@ ngx_rtmp_mp4_write_stco(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype) + ngx_rtmp_mp4_track_type_t ttype, u_char *kid) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "stbl"); - ngx_rtmp_mp4_write_stsd(s, b, ttype); + ngx_rtmp_mp4_write_stsd(s, b, ttype, kid); ngx_rtmp_mp4_write_stts(b); ngx_rtmp_mp4_write_stsc(b); ngx_rtmp_mp4_write_stsz(b); @@ -1072,7 +1072,7 @@ ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype) + ngx_rtmp_mp4_track_type_t ttype, u_char *kid) { u_char *pos; @@ -1086,7 +1086,7 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, } ngx_rtmp_mp4_write_dinf(b); - ngx_rtmp_mp4_write_stbl(s, b, ttype); + ngx_rtmp_mp4_write_stbl(s, b, ttype, kid); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1096,7 +1096,7 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype) + ngx_rtmp_mp4_track_type_t ttype, u_char *kid) { u_char *pos; @@ -1104,7 +1104,7 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_write_mdhd(b); ngx_rtmp_mp4_write_hdlr(b, ttype); - ngx_rtmp_mp4_write_minf(s, b, ttype); + ngx_rtmp_mp4_write_minf(s, b, ttype, kid); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1113,14 +1113,14 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype) + ngx_rtmp_mp4_track_type_t ttype, u_char *kid) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "trak"); ngx_rtmp_mp4_write_tkhd(s, b, ttype); - ngx_rtmp_mp4_write_mdia(s, b, ttype); + ngx_rtmp_mp4_write_mdia(s, b, ttype, kid); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1165,7 +1165,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b) ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype) + ngx_rtmp_mp4_track_type_t ttype, u_char *kid) { u_char *pos; @@ -1173,7 +1173,7 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_write_mvhd(b); ngx_rtmp_mp4_write_mvex(b); - ngx_rtmp_mp4_write_trak(s, b, ttype); + ngx_rtmp_mp4_write_trak(s, b, ttype, kid); ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index c474071ed..55780ded6 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -44,7 +44,7 @@ typedef enum { ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b); ngx_int_t ngx_rtmp_mp4_write_styp(ngx_buf_t *b); ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype); + ngx_rtmp_mp4_track_type_t ttype, u_char *kid); ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected); From 405392dc55ec36c53ac1940cfb0bc5915b9dbfc8 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 20 May 2018 20:15:02 +0000 Subject: [PATCH 072/113] some progress on sig / argh --- TODO | 3 +++ dash/ngx_rtmp_cenc.c | 2 +- dash/ngx_rtmp_cenc.h | 3 +++ dash/ngx_rtmp_dash_templates.h | 7 +++++++ dash/ngx_rtmp_mp4.c | 33 +++++++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 1 deletion(-) diff --git a/TODO b/TODO index 50cefab05..b75e0030d 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,7 @@ - need doc - need re-upstream - test common encryption code + - add support for sub sample encryption + - add sig in manifest + - add sig for wdv ? - rewritte the variant code for dash, using memory ? diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index dd1ef6a0d..c4690fe4c 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -101,7 +101,7 @@ ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, while (left > 0) { - debug_counter(s, counter, key, left); + //debug_counter(s, counter, key, left); EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index afbf3ebe7..108233775 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -3,6 +3,9 @@ #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) +#define NGX_RTMP_CENC_CK_SID \ + "\x10\x77\xef\xec\xc0\xb2\x4d\x02" \ + "\xac\xe3\x3c\x1e\x52\xe2\xfb\x4b" ngx_int_t ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index aa4571736..1fc537a9c 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -40,6 +40,13 @@ " par=\"%ui:%ui\">\n" +#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC \ + " " + + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ " Date: Tue, 22 May 2018 11:12:07 +0000 Subject: [PATCH 073/113] Add content protection in manifest yupi --- dash/ngx_rtmp_cenc.h | 3 --- dash/ngx_rtmp_dash_module.c | 25 ++++++++++++++++++++++++- dash/ngx_rtmp_dash_templates.h | 20 +++++++++++--------- dash/ngx_rtmp_mp4.c | 10 +++++++--- 4 files changed, 42 insertions(+), 16 deletions(-) diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index 108233775..afbf3ebe7 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -3,9 +3,6 @@ #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) -#define NGX_RTMP_CENC_CK_SID \ - "\x10\x77\xef\xec\xc0\xb2\x4d\x02" \ - "\xac\xe3\x3c\x1e\x52\xe2\xfb\x4b" ngx_int_t ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index ba3d3aca3..a94af668f 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -749,6 +749,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; + static u_char kid[NGX_RTMP_CENC_KEY_SIZE]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -762,6 +763,14 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_dash_write_init_segments(s); } + if (dacf->cenc) { + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: error cenc kid is invalid"); + return NGX_ERROR; + } + } + fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -919,7 +928,14 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) case NGX_RTMP_DASH_AD_MARKERS_ON_CUEPOINT_SCTE35: p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - + + if (dacf->cenc) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, + kid[0], kid[1], kid[2], kid[3], + kid[4], kid[5], kid[6], kid[7], + kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + } + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, codec_ctx->avc_profile, @@ -949,6 +965,13 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); + if (dacf->cenc) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, + kid[0], kid[1], kid[2], kid[3], + kid[4], kid[5], kid[6], kid[7], + kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + } + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 1fc537a9c..3293565da 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -23,12 +23,6 @@ " \n" -#define NGX_RTMP_DASH_INBAND_EVENT \ - " \n" - - #define NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_VIDEO \ " \n" +#define NGX_RTMP_DASH_INBAND_EVENT \ + " \n" + + #define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC \ " " + " cenc:default_KID=\"" \ + "%02xd%02xd%02xd%02xd-%02xd%02xd-%02xd%02xd-" \ + "%02xd%02xd-%02xd%02xd%02xd%02xd%02xd%02xd\"\n" \ + " schemeIdUri=\"urn:mpeg:dash:mp4protection:2011\"\n" \ + " value=\"cenc\"/>\n" #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index dfaa9b46b..9ce772ab2 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -537,9 +537,13 @@ ngx_rtmp_mp4_write_schm(ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_pssh(ngx_buf_t *b, u_char *kid) +ngx_rtmp_mp4_write_pssh_ck(ngx_buf_t *b, u_char *kid) { u_char *pos; + u_char ck_sid[] = { + 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, + 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b + }; pos = ngx_rtmp_mp4_start_box(b, "pssh"); @@ -547,7 +551,7 @@ ngx_rtmp_mp4_write_pssh(ngx_buf_t *b, u_char *kid) ngx_rtmp_mp4_field_32(b, 0x01000000); /* system ID : org.w3.clearkey */ - ngx_rtmp_mp4_data(b, NGX_RTMP_CENC_CK_SID, NGX_RTMP_CENC_KEY_SIZE); + ngx_rtmp_mp4_data(b, ck_sid, NGX_RTMP_CENC_KEY_SIZE); /* kid count */ ngx_rtmp_mp4_field_32(b, 1); @@ -1205,7 +1209,7 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK || ttype == NGX_RTMP_MP4_EAUDIO_TRACK) { - ngx_rtmp_mp4_write_pssh(b, kid); + ngx_rtmp_mp4_write_pssh_ck(b, kid); } ngx_rtmp_mp4_update_box_size(b, pos); From 64daef90f16f2dbe21cd5b7f1431927304996912 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 23 May 2018 12:29:42 +0000 Subject: [PATCH 074/113] Add consistancy check --- dash/ngx_rtmp_cenc.c | 6 +++++- dash/ngx_rtmp_dash_templates.h | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index c4690fe4c..20b572323 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -102,7 +102,11 @@ ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, while (left > 0) { //debug_counter(s, counter, key, left); - EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE); + if (EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE) != 1) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash rtmp_cenc_encrypt: evp_encrypt_update failed"); + return NGX_ERROR; + } len = (left < AES_BLOCK_SIZE) ? left : AES_BLOCK_SIZE; for (j = 0; j < len; j++) diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 3293565da..0d1c8644b 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -7,6 +7,7 @@ " Date: Thu, 24 May 2018 19:17:03 +0000 Subject: [PATCH 075/113] Bug fixing thnak you Jacob --- TODO | 4 +++- dash/ngx_rtmp_cenc.c | 12 ++++++------ dash/ngx_rtmp_dash_module.c | 5 +++++ dash/ngx_rtmp_dash_templates.h | 8 ++++++++ dash/ngx_rtmp_mp4.c | 2 +- 5 files changed, 23 insertions(+), 8 deletions(-) diff --git a/TODO b/TODO index b75e0030d..03643de80 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,9 @@ - need doc - need re-upstream - test common encryption code + - allow writing in // + - correct pssh in manifest + - add pssh / cenc in variant mpd - add support for sub sample encryption - - add sig in manifest - add sig for wdv ? - rewritte the variant code for dash, using memory ? diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 20b572323..4fdf8a099 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -77,7 +77,7 @@ ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, { /* aes-ctr implementation */ - EVP_CIPHER_CTX* cipher; + EVP_CIPHER_CTX* ctx; size_t j, len, left = data_len; int i, w; uint8_t *pos = data; @@ -86,14 +86,14 @@ ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, ngx_memset(counter + NGX_RTMP_CENC_IV_SIZE, 0, NGX_RTMP_CENC_IV_SIZE); ngx_memcpy(counter, iv, NGX_RTMP_CENC_IV_SIZE); - cipher = EVP_CIPHER_CTX_new(); - if (cipher == NULL) { + ctx = EVP_CIPHER_CTX_new(); + if (ctx == NULL) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash rtmp_cenc_encrypt: evp_cipher_ctx failed"); return NGX_ERROR; } - if (EVP_EncryptInit_ex(cipher, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { + if (EVP_EncryptInit_ex(ctx, EVP_aes_128_ecb(), NULL, key, NULL) != 1) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash rtmp_cenc_encrypt: evp_encrypt_init failed"); return NGX_ERROR; @@ -102,7 +102,7 @@ ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, while (left > 0) { //debug_counter(s, counter, key, left); - if (EVP_EncryptUpdate(cipher, buf, &w, counter, AES_BLOCK_SIZE) != 1) { + if (EVP_EncryptUpdate(ctx, buf, &w, counter, AES_BLOCK_SIZE) != 1) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash rtmp_cenc_encrypt: evp_encrypt_update failed"); return NGX_ERROR; @@ -121,7 +121,7 @@ ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, } } - EVP_CIPHER_CTX_free(cipher); + EVP_CIPHER_CTX_free(ctx); return NGX_OK; } diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index a94af668f..1c5b939d2 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -760,6 +760,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (ctx->id == 0) { + /* XXX read once the kid and pass it to write_init */ ngx_rtmp_dash_write_init_segments(s); } @@ -934,6 +935,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) kid[0], kid[1], kid[2], kid[3], kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, + "AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAHxDfEN8Q3xDfEN8Q3xDfENAAAAAA=="); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, @@ -970,6 +973,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) kid[0], kid[1], kid[2], kid[3], kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, + "AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAHxDfEN8Q3xDfEN8Q3xDfENAAAAAA=="); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 0d1c8644b..e87b984ff 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -50,6 +50,14 @@ " value=\"cenc\"/>\n" +#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC \ + " \n" \ + " %s\n" \ + " \n" + + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ " Date: Fri, 25 May 2018 20:11:16 +0000 Subject: [PATCH 076/113] Let's go for sub sample... --- dash/ngx_rtmp_cenc.c | 2 +- dash/ngx_rtmp_cenc.h | 2 +- dash/ngx_rtmp_dash_module.c | 18 +++++++++++------- 3 files changed, 13 insertions(+), 9 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 4fdf8a099..456aafdc1 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -72,7 +72,7 @@ ngx_rtmp_cenc_increment_iv(u_char* iv) ngx_int_t -ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, +ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len) { /* aes-ctr implementation */ diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index afbf3ebe7..d6c683814 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -14,7 +14,7 @@ void ngx_rtmp_cenc_increment_iv(u_char* iv); ngx_int_t -ngx_rtmp_cenc_encrypt(ngx_rtmp_session_t *s, +ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len); #endif /* _NGX_RTMP_CENC_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 1c5b939d2..fd5c56425 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1867,7 +1867,17 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { if (t->is_protected) { - ngx_rtmp_cenc_encrypt(s, t->key, t->iv, buffer, size); + if (t->type == 'v') { + ngx_rtmp_cenc_encrypt_full_sample(s, t->key, t->iv, buffer, size); + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: cenc crypt video sample: count=%ui, key=%ui, size=%ui", + t->sample_count, key, size); + } else { + ngx_rtmp_cenc_encrypt_full_sample(s, t->key, t->iv, buffer, size); + ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: cenc crypt audio sample: count=%ui, key=%ui, size=%ui", + t->sample_count, key, size); + } } if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { @@ -1875,12 +1885,6 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, "dash: " ngx_write_fd_n " failed"); return NGX_ERROR; } - - if (t->type == 'v') { - ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: write_video sample: count=%ui, key=%ui, size=%ui", - t->sample_count, key, size); - } smpl = &t->samples[t->sample_count]; From 98e036ecc2e6f92912f9ef4e29982244f5bd3a97 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 27 May 2018 19:50:13 +0000 Subject: [PATCH 077/113] Begin impleme of sub sample --- TODO | 22 +++++++++++++++++++--- dash/ngx_rtmp_cenc.c | 32 +++++++++++++++++++++++++++++++- dash/ngx_rtmp_cenc.h | 6 ++++++ dash/ngx_rtmp_dash_module.c | 10 +++++----- 4 files changed, 61 insertions(+), 9 deletions(-) diff --git a/TODO b/TODO index 03643de80..978ab4c2f 100644 --- a/TODO +++ b/TODO @@ -1,9 +1,25 @@ - need doc - need re-upstream - test common encryption code - - allow writing in // - - correct pssh in manifest - - add pssh / cenc in variant mpd + - refacto code for init / kid + - correct pssh in manifest // OK + - add pssh / cenc in variant mpd // OK + - add real base64 encrypt pssh data - add support for sub sample encryption - add sig for wdv ? - rewritte the variant code for dash, using memory ? + +MIN_CLEAR_BYTES = 48 + +if (size <= MIN_CLEAR_BYTES) { + clear_size = size + encrypted_size = 0 + return +} + +encrypted_size = 16 * ceil ((size - MIN_CLEAR_BYTES) / 16 ) + +4444 +4384 +60 + diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 456aafdc1..de5bc7651 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -72,7 +72,7 @@ ngx_rtmp_cenc_increment_iv(u_char* iv) ngx_int_t -ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, +ngx_rtmp_cenc_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len) { /* aes-ctr implementation */ @@ -127,3 +127,33 @@ ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t * } +ngx_int_t +ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, + uint8_t *data, size_t data_len) +{ + return ngx_rtmp_cenc_aes_ctr_encrypt(s, key, iv, data, data_len); +} + + +ngx_int_t +ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, + uint8_t *data, size_t data_len, size_t *clear_data_len) +{ + size_t crypted_data_len; + + /* small sample leave it in clear */ + if (data_len <= NGX_RTMP_CENC_MIN_CLEAR) { + *clear_data_len = data_len; + return NGX_OK; + } + + /* skip sufficient amount of data to leave nalu header/infos + * in clear to conform to the norm */ + crypted_data_len = + AES_BLOCK_SIZE * ((data_len - NGX_RTMP_CENC_MIN_CLEAR) / AES_BLOCK_SIZE); + *clear_data_len = data_len - crypted_data_len; + + data += *clear_data_len; + return ngx_rtmp_cenc_aes_ctr_encrypt(s, key, iv, data, crypted_data_len); + +} diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index d6c683814..1b37812a7 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -3,6 +3,7 @@ #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) +#define NGX_RTMP_CENC_MIN_CLEAR (48) ngx_int_t ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); @@ -17,4 +18,9 @@ ngx_int_t ngx_rtmp_cenc_encrypt_full_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len); +ngx_int_t +ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, + uint8_t *key, uint8_t *iv, uint8_t *data, + size_t data_len, size_t *clear_data_len); + #endif /* _NGX_RTMP_CENC_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index fd5c56425..f94681d30 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1834,7 +1834,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, ngx_rtmp_dash_track_t *t, ngx_int_t key, uint32_t timestamp, uint32_t delay) { u_char *p; - size_t size, bsize; + size_t size, bsize, csize; ngx_rtmp_mp4_sample_t *smpl; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; @@ -1868,10 +1868,10 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, if (t->is_protected) { if (t->type == 'v') { - ngx_rtmp_cenc_encrypt_full_sample(s, t->key, t->iv, buffer, size); - ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: cenc crypt video sample: count=%ui, key=%ui, size=%ui", - t->sample_count, key, size); + ngx_rtmp_cenc_encrypt_sub_sample(s, t->key, t->iv, buffer, size, &csize); + ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: cenc crypt video sample: count=%ui, key=%ui, size=%ui, csize=%ui", + t->sample_count, key, size, csize); } else { ngx_rtmp_cenc_encrypt_full_sample(s, t->key, t->iv, buffer, size); ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, From d8bf80baba64ccecc9fa2ee25263a56117e8e02c Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 28 May 2018 12:29:39 +0000 Subject: [PATCH 078/113] Working sub-sample ? --- dash/ngx_rtmp_cenc.c | 2 +- dash/ngx_rtmp_dash_module.c | 5 +++- dash/ngx_rtmp_mp4.c | 55 ++++++++++++++++++++++++++----------- dash/ngx_rtmp_mp4.h | 3 +- 4 files changed, 46 insertions(+), 19 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index de5bc7651..b403ca66d 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -150,7 +150,7 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *i /* skip sufficient amount of data to leave nalu header/infos * in clear to conform to the norm */ crypted_data_len = - AES_BLOCK_SIZE * ((data_len - NGX_RTMP_CENC_MIN_CLEAR) / AES_BLOCK_SIZE); + ((data_len - NGX_RTMP_CENC_MIN_CLEAR) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; *clear_data_len = data_len - crypted_data_len; data += *clear_data_len; diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index f94681d30..d0991b313 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1236,7 +1236,7 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) pos = b.last; b.last += 44; /* leave room for sidx */ - ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, + ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->type, t->sample_count, t->samples, t->sample_mask, t->id, t->is_protected); pos1 = b.last; b.last = pos; @@ -1898,6 +1898,9 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, smpl->is_protected = 1; ngx_memcpy(smpl->iv, t->iv, NGX_RTMP_CENC_IV_SIZE); ngx_rtmp_cenc_increment_iv(t->iv); + if (t->type == 'v') { + smpl->clear_size = (uint32_t) csize; + } } if (t->sample_count > 0) { diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 9dcbeac24..b58679555 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1255,9 +1255,9 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, uint32_t earliest_pres_time) static ngx_int_t -ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, - ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, - u_char *moof_pos, ngx_flag_t is_protected) +ngx_rtmp_mp4_write_trun(ngx_buf_t *b, char type, + uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, + ngx_uint_t sample_mask, u_char *moof_pos, ngx_flag_t is_protected) { u_char *pos; uint32_t i, offset, nitems, flags; @@ -1291,11 +1291,15 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, if (is_protected) { /* if cenc is enabled we neeed to add - * saiz(17) saio(20) senc(16 + sc*8) - * to the data offset */ + * saiz saiz senc size to the data offset */ offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4); - offset += 17 + 20 + 16 + (sample_count * NGX_RTMP_CENC_IV_SIZE) + 8; - + if (type == 'v') { + /* video use sub sample senc */ + offset += 17 + 20 + 16 + (sample_count * (NGX_RTMP_CENC_IV_SIZE + 8)) + 8; + } else { + /* audio use full sample senc */ + offset += 17 + 20 + 16 + (sample_count * NGX_RTMP_CENC_IV_SIZE) + 8; + } } else { offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8; } @@ -1376,8 +1380,8 @@ ngx_rtmp_mp4_write_saio(ngx_buf_t *b, u_char *moof_pos) static ngx_int_t -ngx_rtmp_mp4_write_senc(ngx_buf_t *b, uint32_t sample_count, - ngx_rtmp_mp4_sample_t *samples) +ngx_rtmp_mp4_write_senc(ngx_buf_t *b, char type, + uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples) { u_char *pos; uint32_t i; @@ -1385,7 +1389,13 @@ ngx_rtmp_mp4_write_senc(ngx_buf_t *b, uint32_t sample_count, pos = ngx_rtmp_mp4_start_box(b, "senc"); /* version & flag */ - ngx_rtmp_mp4_field_32(b, 0); + if (type == 'v') { + /* video use sub_sample flag 0x02 */ + ngx_rtmp_mp4_field_32(b, 0x02); + } else { + /* audio use full_sample flag 0x00 */ + ngx_rtmp_mp4_field_32(b, 0); + } /* sample count */ ngx_rtmp_mp4_field_32(b, sample_count); @@ -1395,6 +1405,19 @@ ngx_rtmp_mp4_write_senc(ngx_buf_t *b, uint32_t sample_count, /* IV per sample */ ngx_rtmp_mp4_data(b, samples->iv, NGX_RTMP_CENC_IV_SIZE); + /* subsample informations */ + if (type == 'v') { + + /* sub sample count */ + ngx_rtmp_mp4_field_16(b, 1); + + /* sub sample clear data size */ + ngx_rtmp_mp4_field_16(b, samples->clear_size); + + /* sub sample protected data size */ + ngx_rtmp_mp4_field_32(b, samples->size - samples->clear_size); + + } } ngx_rtmp_mp4_update_box_size(b, pos); @@ -1405,7 +1428,7 @@ ngx_rtmp_mp4_write_senc(ngx_buf_t *b, uint32_t sample_count, static ngx_int_t ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, - uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, + char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, u_char *moof_pos, ngx_flag_t is_protected) { u_char *pos; @@ -1414,13 +1437,13 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, ngx_rtmp_mp4_write_tfhd(b); ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time); - ngx_rtmp_mp4_write_trun(b, sample_count, samples, sample_mask, + ngx_rtmp_mp4_write_trun(b, type, sample_count, samples, sample_mask, moof_pos, is_protected); if (is_protected) { ngx_rtmp_mp4_write_saiz(b, sample_count); ngx_rtmp_mp4_write_saio(b, moof_pos); - ngx_rtmp_mp4_write_senc(b, sample_count, samples); + ngx_rtmp_mp4_write_senc(b, type, sample_count, samples); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -1500,7 +1523,7 @@ ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, - uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, + char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected) { u_char *pos; @@ -1508,8 +1531,8 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, pos = ngx_rtmp_mp4_start_box(b, "moof"); ngx_rtmp_mp4_write_mfhd(b, index); - ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, samples, - sample_mask, pos, is_protected); + ngx_rtmp_mp4_write_traf(b, earliest_pres_time, type, sample_count, + samples, sample_mask, pos, is_protected); ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 55780ded6..5161767c1 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -18,6 +18,7 @@ typedef struct { uint32_t size; + uint32_t clear_size; uint32_t duration; uint32_t delay; uint32_t timestamp; @@ -46,7 +47,7 @@ ngx_int_t ngx_rtmp_mp4_write_styp(ngx_buf_t *b); ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_track_type_t ttype, u_char *kid); ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, - uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, + char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected); ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, uint32_t earliest_pres_time, From 4c8171abbea9edb3c1e1588daf5ff22a5d4fa7db Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 29 May 2018 20:05:35 +0000 Subject: [PATCH 079/113] well very close but... --- dash/ngx_rtmp_mp4.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index b58679555..bb5b5358a 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1334,7 +1334,7 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, char type, static ngx_int_t -ngx_rtmp_mp4_write_saiz(ngx_buf_t *b, uint32_t sample_count) +ngx_rtmp_mp4_write_saiz(ngx_buf_t *b, char type, uint32_t sample_count) { u_char *pos; @@ -1344,7 +1344,13 @@ ngx_rtmp_mp4_write_saiz(ngx_buf_t *b, uint32_t sample_count) ngx_rtmp_mp4_field_32(b, 0); /* defaut sample info size */ - ngx_rtmp_mp4_field_8(b, 8); + if (type == 'v') { + /* sub sample */ + ngx_rtmp_mp4_field_8(b, NGX_RTMP_CENC_IV_SIZE + 8); + } else { + /* full sample */ + ngx_rtmp_mp4_field_8(b, NGX_RTMP_CENC_IV_SIZE); + } /* sample count */ ngx_rtmp_mp4_field_32(b, sample_count); @@ -1441,7 +1447,7 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, moof_pos, is_protected); if (is_protected) { - ngx_rtmp_mp4_write_saiz(b, sample_count); + ngx_rtmp_mp4_write_saiz(b, type, sample_count); ngx_rtmp_mp4_write_saio(b, moof_pos); ngx_rtmp_mp4_write_senc(b, type, sample_count, samples); } From 1a55c304bfbf3647635b2e4b33558da9c354fefd Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 29 May 2018 21:18:14 +0000 Subject: [PATCH 080/113] Magic number :) --- dash/ngx_rtmp_cenc.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index 1b37812a7..cd568a8d9 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -3,7 +3,7 @@ #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) -#define NGX_RTMP_CENC_MIN_CLEAR (48) +#define NGX_RTMP_CENC_MIN_CLEAR (400) ngx_int_t ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); From 012c94567643cb12f8db948a4e1de2d206721a66 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 30 May 2018 14:24:45 +0000 Subject: [PATCH 081/113] Yupi working --- TODO | 16 +--------------- dash/ngx_rtmp_cenc.h | 2 +- 2 files changed, 2 insertions(+), 16 deletions(-) diff --git a/TODO b/TODO index 978ab4c2f..1685b5024 100644 --- a/TODO +++ b/TODO @@ -5,21 +5,7 @@ - correct pssh in manifest // OK - add pssh / cenc in variant mpd // OK - add real base64 encrypt pssh data - - add support for sub sample encryption + - add support for sub sample encryption //OK - add sig for wdv ? - rewritte the variant code for dash, using memory ? -MIN_CLEAR_BYTES = 48 - -if (size <= MIN_CLEAR_BYTES) { - clear_size = size - encrypted_size = 0 - return -} - -encrypted_size = 16 * ceil ((size - MIN_CLEAR_BYTES) / 16 ) - -4444 -4384 -60 - diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index cd568a8d9..fc8d2aa79 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -3,7 +3,7 @@ #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) -#define NGX_RTMP_CENC_MIN_CLEAR (400) +#define NGX_RTMP_CENC_MIN_CLEAR (100) ngx_int_t ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); From def8fb4c8360d2d845b92d19061e5e85dc1058f5 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 30 May 2018 14:41:27 +0000 Subject: [PATCH 082/113] Fix where there is no variant --- dash/ngx_rtmp_dash_module.c | 57 +++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 31 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index d0991b313..73f7e2c27 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -23,7 +23,8 @@ static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf); static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf); static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child); -static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s); +static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, + u_char *kid); static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s); @@ -590,25 +591,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) for (j = 0; j < dacf->variant->nelts; j++, var++) { - p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, - &ctx->varname, &var->suffix, - codec_ctx->avc_profile, - codec_ctx->avc_compat, - codec_ctx->avc_level); - - arg = var->args.elts; - for (k = 0; k < var->args.nelts && k < 3 ; k++, arg++) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); - } - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO, - &ctx->varname, &var->suffix, sep, - &ctx->varname, &var->suffix, sep); - - n = ngx_write_fd(fd, buffer, p - buffer); - /* read segments file */ if (dacf->nested) { *ngx_sprintf(seg_path, "%V/%V%V/index.seg", @@ -629,6 +611,25 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) continue; } + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VARIANT_VIDEO, + &ctx->varname, &var->suffix, + codec_ctx->avc_profile, + codec_ctx->avc_compat, + codec_ctx->avc_level); + + arg = var->args.elts; + for (k = 0; k < var->args.nelts && k < 3 ; k++, arg++) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG, arg); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VARIANT_ARG_FOOTER); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_SEGMENTTPL_VARIANT_VIDEO, + &ctx->varname, &var->suffix, sep, + &ctx->varname, &var->suffix, sep); + + n = ngx_write_fd(fd, buffer, p - buffer); + while ((n = ngx_read_fd(fds, buffer, sizeof(buffer)))) { n = ngx_write_fd(fd, buffer, n); } @@ -759,10 +760,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } - if (ctx->id == 0) { - /* XXX read once the kid and pass it to write_init */ - ngx_rtmp_dash_write_init_segments(s); - } if (dacf->cenc) { if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { @@ -772,6 +769,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } } + if (ctx->id == 0) { + ngx_rtmp_dash_write_init_segments(s, kid); + } + fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -1068,7 +1069,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) static ngx_int_t -ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) +ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, u_char *kid) { ngx_fd_t fd; ngx_int_t rc; @@ -1078,7 +1079,6 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) ngx_rtmp_dash_app_conf_t *dacf; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - static u_char kid[NGX_RTMP_CENC_KEY_SIZE]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -1107,11 +1107,6 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) ngx_rtmp_mp4_write_ftyp(&b); if (dacf->cenc) { - if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: error cenc kid is invalid"); - return NGX_ERROR; - } ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EVIDEO_TRACK, kid); } else { ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK, NULL); From d7be1fb59a9a5bd0b3b50dc3425b82c4556638cc Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 30 May 2018 21:27:00 +0000 Subject: [PATCH 083/113] Real base64 encoding --- TODO | 4 ++-- dash/ngx_rtmp_cenc.c | 38 ++++++++++++++++++++++++++++++++++++- dash/ngx_rtmp_cenc.h | 4 ++++ dash/ngx_rtmp_dash_module.c | 36 ++++++++++++++++++++++++++++++++--- 4 files changed, 76 insertions(+), 6 deletions(-) diff --git a/TODO b/TODO index 1685b5024..57102bc3d 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,10 @@ - need doc - need re-upstream - test common encryption code - - refacto code for init / kid + - refacto code for init / kid //OK - correct pssh in manifest // OK - add pssh / cenc in variant mpd // OK - - add real base64 encrypt pssh data + - add real base64 encrypt pssh data // OK need refacto - add support for sub sample encryption //OK - add sig for wdv ? - rewritte the variant code for dash, using memory ? diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index b403ca66d..3aa74e567 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -141,7 +141,7 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *i { size_t crypted_data_len; - /* small sample leave it in clear */ + /* small sample : leave it in clear */ if (data_len <= NGX_RTMP_CENC_MIN_CLEAR) { *clear_data_len = data_len; return NGX_OK; @@ -157,3 +157,39 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *i return ngx_rtmp_cenc_aes_ctr_encrypt(s, key, iv, data, crypted_data_len); } + + +ngx_int_t +ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, + ngx_str_t *dest_pssh) +{ + ngx_str_t src_pssh; + + u_char pssh[] = { + 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, // pssh box header + 0x01, 0x00, 0x00, 0x00, // header + 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, // systemID + 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b, + 0x00, 0x00, 0x00, 0x01, // kid count + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, // kid + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00 // data size + }; + + ngx_memcpy(pssh+32, kid, NGX_RTMP_CENC_KEY_SIZE); + + src_pssh.len = sizeof(pssh); + src_pssh.data = pssh; + + dest_pssh->len = ngx_base64_encoded_length(src_pssh.len); + dest_pssh->data = ngx_palloc(s->connection->pool, dest_pssh->len + 1); + + ngx_encode_base64(dest_pssh, &src_pssh); + dest_pssh->data[dest_pssh->len] = '\0'; + + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash cenc pssh: %ui %s ", dest_pssh->len, dest_pssh->data); + + return NGX_OK; +} + diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index fc8d2aa79..6fc3b2fbf 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -23,4 +23,8 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, uint8_t *data, size_t data_len, size_t *clear_data_len); +ngx_int_t +ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, + u_char* kid, ngx_str_t *dest_pssh); + #endif /* _NGX_RTMP_CENC_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 73f7e2c27..631cb582b 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -411,6 +411,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ssize_t n; ngx_fd_t fd, fds; struct tm tm; + ngx_str_t cenc_pssh; ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; @@ -432,6 +433,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; static u_char seg_path[NGX_MAX_PATH + 1]; + static u_char kid[NGX_RTMP_CENC_KEY_SIZE]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -441,6 +443,15 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } + if (dacf->cenc) { + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "dash: error cenc kid is invalid"); + return NGX_ERROR; + } + ngx_rtmp_content_protection_pssh(s, kid, &cenc_pssh); + } + fd = ngx_open_file(ctx->var_playlist_bak.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -587,6 +598,15 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } + if (dacf->cenc) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, + kid[0], kid[1], kid[2], kid[3], + kid[4], kid[5], kid[6], kid[7], + kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, + cenc_pssh.data); + } + n = ngx_write_fd(fd, buffer, p - buffer); for (j = 0; j < dacf->variant->nelts; j++, var++) { @@ -648,6 +668,15 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); + if (dacf->cenc) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, + kid[0], kid[1], kid[2], kid[3], + kid[4], kid[5], kid[6], kid[7], + kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, + cenc_pssh.data); + } + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? @@ -742,6 +771,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_dash_frag_t *f; ngx_rtmp_dash_app_conf_t *dacf; + ngx_str_t cenc_pssh; ngx_rtmp_playlist_t v; @@ -760,13 +790,13 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } - if (dacf->cenc) { if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc kid is invalid"); return NGX_ERROR; } + ngx_rtmp_content_protection_pssh(s, kid, &cenc_pssh); } if (ctx->id == 0) { @@ -937,7 +967,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - "AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAHxDfEN8Q3xDfEN8Q3xDfENAAAAAA=="); + cenc_pssh.data); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, @@ -975,7 +1005,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - "AAAANHBzc2gBAAAAEHfv7MCyTQKs4zweUuL7SwAAAAHxDfEN8Q3xDfEN8Q3xDfENAAAAAA=="); + cenc_pssh.data); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, From 1eb019fd3c74d015cde5c9743560e0bb572d9fde Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Fri, 1 Jun 2018 08:55:52 +0000 Subject: [PATCH 084/113] Preliminary support for widevine --- dash/ngx_rtmp_cenc.c | 4 +--- dash/ngx_rtmp_dash_module.c | 35 ++++++++++++++++++++++++++++++++++ dash/ngx_rtmp_dash_templates.h | 8 ++++++++ 3 files changed, 44 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 3aa74e567..513074f3a 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -163,6 +163,7 @@ ngx_int_t ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, ngx_str_t *dest_pssh) { + // static alloc ? ngx_str_t src_pssh; u_char pssh[] = { @@ -187,9 +188,6 @@ ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, ngx_encode_base64(dest_pssh, &src_pssh); dest_pssh->data[dest_pssh->len] = '\0'; - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash cenc pssh: %ui %s ", dest_pssh->len, dest_pssh->data); - return NGX_OK; } diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 631cb582b..e857a7f59 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -141,6 +141,7 @@ typedef struct { ngx_flag_t cenc; ngx_str_t cenc_key; ngx_str_t cenc_kid; + ngx_flag_t wdv; ngx_flag_t repetition; ngx_uint_t clock_compensation; // Try to compensate clock drift // between client and server (on client side) @@ -228,6 +229,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, cenc_kid), NULL }, + { ngx_string("dash_wdv"), + 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_dash_app_conf_t, wdv), + NULL }, + { ngx_string("dash_clock_compensation"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -603,9 +611,16 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) kid[0], kid[1], kid[2], kid[3], kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, cenc_pssh.data); + + if (dacf->wdv) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, + "xxx"); + } } + n = ngx_write_fd(fd, buffer, p - buffer); @@ -673,8 +688,14 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) kid[0], kid[1], kid[2], kid[3], kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, cenc_pssh.data); + + if (dacf->wdv) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, + "xxx"); + } } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, @@ -966,8 +987,14 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) kid[0], kid[1], kid[2], kid[3], kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, cenc_pssh.data); + + if (dacf->wdv) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, + "xxx"); + } } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, @@ -1004,8 +1031,14 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) kid[0], kid[1], kid[2], kid[3], kid[4], kid[5], kid[6], kid[7], kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, cenc_pssh.data); + + if (dacf->wdv) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, + "xxx"); + } } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, @@ -2645,6 +2678,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) conf->nested = NGX_CONF_UNSET; conf->repetition = NGX_CONF_UNSET; conf->cenc = NGX_CONF_UNSET; + conf->wdv = NGX_CONF_UNSET; conf->clock_compensation = NGX_CONF_UNSET; conf->ad_markers = NGX_CONF_UNSET; @@ -2668,6 +2702,7 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->cenc, prev->cenc, 0); ngx_conf_merge_str_value(conf->cenc_key, prev->cenc_key, ""); ngx_conf_merge_str_value(conf->cenc_kid, prev->cenc_kid, ""); + ngx_conf_merge_value(conf->wdv, prev->wdv, 0); ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index e87b984ff..4ae50076e 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -58,6 +58,14 @@ " \n" +#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV \ + " \n" \ + " %s\n" \ + " \n" + + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ " Date: Sun, 3 Jun 2018 09:48:49 +0000 Subject: [PATCH 085/113] Add configuration for widevine --- dash/ngx_rtmp_cenc.c | 4 +--- dash/ngx_rtmp_dash_module.c | 18 +++++++++++++----- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 513074f3a..a990f6989 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -37,8 +37,8 @@ ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst) for (i = 0; i < NGX_RTMP_CENC_KEY_SIZE; i++) { l = tolower(src.data[i*2]); - h = tolower(src.data[i*2+1]); l = l >= 'a' ? l - 'a' + 10 : l - '0'; + h = tolower(src.data[i*2+1]); h = h >= 'a' ? h - 'a' + 10 : h - '0'; dst[i] = (l << 4) | h; } @@ -101,7 +101,6 @@ ngx_rtmp_cenc_aes_ctr_encrypt(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *iv, while (left > 0) { - //debug_counter(s, counter, key, left); if (EVP_EncryptUpdate(ctx, buf, &w, counter, AES_BLOCK_SIZE) != 1) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash rtmp_cenc_encrypt: evp_encrypt_update failed"); @@ -163,7 +162,6 @@ ngx_int_t ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, ngx_str_t *dest_pssh) { - // static alloc ? ngx_str_t src_pssh; u_char pssh[] = { diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e857a7f59..db14395a4 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -142,6 +142,7 @@ typedef struct { ngx_str_t cenc_key; ngx_str_t cenc_kid; ngx_flag_t wdv; + ngx_str_t wdv_data; ngx_flag_t repetition; ngx_uint_t clock_compensation; // Try to compensate clock drift // between client and server (on client side) @@ -236,6 +237,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, wdv), NULL }, + { ngx_string("dash_wdv_data"), + 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_dash_app_conf_t, wdv_data), + NULL }, + { ngx_string("dash_clock_compensation"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -617,10 +625,9 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) if (dacf->wdv) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - "xxx"); + dacf-> wdv_data.data); } } - n = ngx_write_fd(fd, buffer, p - buffer); @@ -694,7 +701,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) if (dacf->wdv) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - "xxx"); + dacf->wdv_data.data); } } @@ -993,7 +1000,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (dacf->wdv) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - "xxx"); + dacf->wdv_data.data); } } @@ -1037,7 +1044,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (dacf->wdv) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - "xxx"); + dacf->wdv_data.data); } } @@ -2703,6 +2710,7 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->cenc_key, prev->cenc_key, ""); ngx_conf_merge_str_value(conf->cenc_kid, prev->cenc_kid, ""); ngx_conf_merge_value(conf->wdv, prev->wdv, 0); + ngx_conf_merge_str_value(conf->wdv_data, prev->wdv_data, ""); ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); From 82ed592a361b02e92998660c4bbe3538bed57bf5 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 3 Jun 2018 09:54:32 +0000 Subject: [PATCH 086/113] near the end --- TODO | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/TODO b/TODO index 57102bc3d..9ac72f9b6 100644 --- a/TODO +++ b/TODO @@ -1,11 +1,17 @@ -- need doc +- doc - need re-upstream -- test common encryption code +- rewritte the variant code for dash, using memory ? + +- test common encryption code (OK clearkey, Widevine both on chrome/firefox!) + - need to write some doc about cenc/drm implem + - refacto code for writing content protection in manifest + - clarify the use of %V in ngx_printf - refacto code for init / kid //OK - correct pssh in manifest // OK - add pssh / cenc in variant mpd // OK - add real base64 encrypt pssh data // OK need refacto - add support for sub sample encryption //OK - - add sig for wdv ? -- rewritte the variant code for dash, using memory ? + - add sig for wdv //OK + - add pssh in init file for wdv + - add msplayready support From cdcbf33a3260d82e999867c46195c838773767d6 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sun, 3 Jun 2018 15:23:20 +0000 Subject: [PATCH 087/113] Use ngx default function --- dash/ngx_rtmp_cenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index a990f6989..aa9336e79 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -36,9 +36,9 @@ ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst) } for (i = 0; i < NGX_RTMP_CENC_KEY_SIZE; i++) { - l = tolower(src.data[i*2]); + l = ngx_tolower(src.data[i*2]); l = l >= 'a' ? l - 'a' + 10 : l - '0'; - h = tolower(src.data[i*2+1]); + h = ngx_tolower(src.data[i*2+1]); h = h >= 'a' ? h - 'a' + 10 : h - '0'; dst[i] = (l << 4) | h; } From 540d45c6fbedd34d985a1114c2cad63fd2a57c01 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 4 Jun 2018 15:52:29 +0000 Subject: [PATCH 088/113] A bit of refacto --- dash/ngx_rtmp_cenc.c | 1 + dash/ngx_rtmp_dash_module.c | 91 +++++++++++++------------------------ 2 files changed, 32 insertions(+), 60 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index aa9336e79..468a1800d 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -185,6 +185,7 @@ ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, ngx_encode_base64(dest_pssh, &src_pssh); dest_pssh->data[dest_pssh->len] = '\0'; + dest_pssh->len += 1; return NGX_OK; } diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index db14395a4..b30441673 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -419,6 +419,33 @@ ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t } +static u_char * +ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, ngx_rtmp_dash_app_conf_t *dacf, + u_char *kid, u_char *p, u_char *last) +{ + ngx_str_t cenc_pssh; + + if (dacf->cenc) { + ngx_rtmp_content_protection_pssh(s, kid, &cenc_pssh); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, + kid[0], kid[1], kid[2], kid[3], + kid[4], kid[5], kid[6], kid[7], + kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, + cenc_pssh.data); + + if (dacf->wdv) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, + dacf->wdv_data.data); + } + } + + return p; +} + + static ngx_int_t ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) { @@ -427,7 +454,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ssize_t n; ngx_fd_t fd, fds; struct tm tm; - ngx_str_t cenc_pssh; ngx_uint_t i, j, k, frame_rate_num, frame_rate_denom; ngx_uint_t depth_msec, depth_sec; ngx_uint_t update_period, update_period_msec; @@ -465,7 +491,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) "dash: error cenc kid is invalid"); return NGX_ERROR; } - ngx_rtmp_content_protection_pssh(s, kid, &cenc_pssh); } fd = ngx_open_file(ctx->var_playlist_bak.data, NGX_FILE_WRONLY, @@ -614,20 +639,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - if (dacf->cenc) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, - kid[0], kid[1], kid[2], kid[3], - kid[4], kid[5], kid[6], kid[7], - kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - cenc_pssh.data); - - if (dacf->wdv) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - dacf-> wdv_data.data); - } - } + p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); n = ngx_write_fd(fd, buffer, p - buffer); @@ -690,20 +702,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - if (dacf->cenc) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, - kid[0], kid[1], kid[2], kid[3], - kid[4], kid[5], kid[6], kid[7], - kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - cenc_pssh.data); - - if (dacf->wdv) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - dacf->wdv_data.data); - } - } + p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, @@ -799,7 +798,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_dash_frag_t *f; ngx_rtmp_dash_app_conf_t *dacf; - ngx_str_t cenc_pssh; ngx_rtmp_playlist_t v; @@ -824,7 +822,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) "dash: error cenc kid is invalid"); return NGX_ERROR; } - ngx_rtmp_content_protection_pssh(s, kid, &cenc_pssh); } if (ctx->id == 0) { @@ -989,20 +986,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - if (dacf->cenc) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, - kid[0], kid[1], kid[2], kid[3], - kid[4], kid[5], kid[6], kid[7], - kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - cenc_pssh.data); - - if (dacf->wdv) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - dacf->wdv_data.data); - } - } + p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, @@ -1033,20 +1017,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - if (dacf->cenc) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, - kid[0], kid[1], kid[2], kid[3], - kid[4], kid[5], kid[6], kid[7], - kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); - - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - cenc_pssh.data); - - if (dacf->wdv) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - dacf->wdv_data.data); - } - } + p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, From 11c1d556ce013c6a49058c1374c5d04f9ba9f162 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 4 Jun 2018 16:20:30 +0000 Subject: [PATCH 089/113] Yurray --- README.md | 4 +++- TODO | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 12e0ba8d7..ec5f95543 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,14 @@ # NGINX-based Media Streaming Server -## nginx-rtmp-module +## nginx-rtmp-module (dash enhanced version) Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. - add the possibility to have dash variant like in HLS (show below configuration, using ffmpeg to trancode in 3 variants). note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest. - add the support of inband scte event, from rtmp AMF event to dash (InbandEvent in manifest and emsg box in mp4 fragment) + - add the support of using repetition in manifest to shorten them + - add the support of common-encryption; currently working DRM are ClearKey and Widevine (see specific doc Here) ``` rtmp { diff --git a/TODO b/TODO index 9ac72f9b6..41b294165 100644 --- a/TODO +++ b/TODO @@ -4,7 +4,7 @@ - test common encryption code (OK clearkey, Widevine both on chrome/firefox!) - need to write some doc about cenc/drm implem - - refacto code for writing content protection in manifest + - refacto code for writing content protection in manifest // SEMI OK - clarify the use of %V in ngx_printf - refacto code for init / kid //OK - correct pssh in manifest // OK From 820a2343e8b11b38ca5b4964d0f844047dba5979 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 5 Jun 2018 21:10:30 +0000 Subject: [PATCH 090/113] Correct %V madness --- TODO | 6 +++--- dash/ngx_rtmp_cenc.c | 4 +--- dash/ngx_rtmp_dash_module.c | 4 ++-- dash/ngx_rtmp_dash_templates.h | 4 ++-- 4 files changed, 8 insertions(+), 10 deletions(-) diff --git a/TODO b/TODO index 41b294165..a5b167b00 100644 --- a/TODO +++ b/TODO @@ -5,11 +5,11 @@ - test common encryption code (OK clearkey, Widevine both on chrome/firefox!) - need to write some doc about cenc/drm implem - refacto code for writing content protection in manifest // SEMI OK - - clarify the use of %V in ngx_printf - - refacto code for init / kid //OK + - clarify the use of %V in ngx_printf // OK + - refacto code for init / kid // OK - correct pssh in manifest // OK - add pssh / cenc in variant mpd // OK - - add real base64 encrypt pssh data // OK need refacto + - add real base64 encrypt pssh data // OK - add support for sub sample encryption //OK - add sig for wdv //OK - add pssh in init file for wdv diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 468a1800d..302fad456 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -181,11 +181,9 @@ ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, src_pssh.data = pssh; dest_pssh->len = ngx_base64_encoded_length(src_pssh.len); - dest_pssh->data = ngx_palloc(s->connection->pool, dest_pssh->len + 1); + dest_pssh->data = ngx_palloc(s->connection->pool, dest_pssh->len); ngx_encode_base64(dest_pssh, &src_pssh); - dest_pssh->data[dest_pssh->len] = '\0'; - dest_pssh->len += 1; return NGX_OK; } diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index b30441673..89e345f76 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -434,11 +434,11 @@ ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, ngx_rtmp_dash_app_ kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - cenc_pssh.data); + &cenc_pssh); if (dacf->wdv) { p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - dacf->wdv_data.data); + &dacf->wdv_data); } } diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 4ae50076e..8adb77311 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -54,7 +54,7 @@ " \n" \ - " %s\n" \ + " %V\n" \ " \n" @@ -62,7 +62,7 @@ " \n" \ - " %s\n" \ + " %V\n" \ " \n" From ebec59d6d3b97b256dc52fe9a084fbbf9b7a483a Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 7 Jun 2018 15:30:45 +0000 Subject: [PATCH 091/113] Refacto --- TODO | 1 + dash/ngx_rtmp_cenc.c | 2 +- dash/ngx_rtmp_cenc.h | 13 ++++++++- dash/ngx_rtmp_dash_module.c | 36 ++++++++++++++----------- dash/ngx_rtmp_mp4.c | 53 +++++++++++++++++++------------------ dash/ngx_rtmp_mp4.h | 2 +- 6 files changed, 62 insertions(+), 45 deletions(-) diff --git a/TODO b/TODO index a5b167b00..c81e96eec 100644 --- a/TODO +++ b/TODO @@ -12,6 +12,7 @@ - add real base64 encrypt pssh data // OK - add support for sub sample encryption //OK - add sig for wdv //OK + - add struct for drm info // in progress - add pssh in init file for wdv - add msplayready support diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 302fad456..3dba47e62 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -159,7 +159,7 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *i ngx_int_t -ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, +ngx_rtmp_cenc_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, ngx_str_t *dest_pssh) { ngx_str_t src_pssh; diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index 6fc3b2fbf..eb62405fe 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -1,10 +1,21 @@ #ifndef _NGX_RTMP_CENC_H_INCLUDED_ #define _NGX_RTMP_CENC_H_INCLUDED_ + #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) #define NGX_RTMP_CENC_MIN_CLEAR (100) + +typedef struct { + unsigned wdv:1; + ngx_str_t wdv_pssh; + unsigned mspr:1; + ngx_str_t mspr_pssh; + u_char kid[NGX_RTMP_CENC_KEY_SIZE]; +} ngx_rtmp_cenc_drm_info_t; + + ngx_int_t ngx_rtmp_cenc_read_hex(ngx_str_t src, u_char* dst); @@ -24,7 +35,7 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, size_t data_len, size_t *clear_data_len); ngx_int_t -ngx_rtmp_content_protection_pssh(ngx_rtmp_session_t *s, +ngx_rtmp_cenc_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, ngx_str_t *dest_pssh); #endif /* _NGX_RTMP_CENC_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 89e345f76..1c93049f9 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -24,7 +24,7 @@ static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf); static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child); static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, - u_char *kid); + ngx_rtmp_cenc_drm_info_t *drmi); static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s); @@ -421,12 +421,15 @@ ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t static u_char * ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, ngx_rtmp_dash_app_conf_t *dacf, - u_char *kid, u_char *p, u_char *last) + ngx_rtmp_cenc_drm_info_t *drmi, u_char *p, u_char *last) { - ngx_str_t cenc_pssh; + u_char *kid; + ngx_str_t cenc_pssh; + + kid = drmi->kid; if (dacf->cenc) { - ngx_rtmp_content_protection_pssh(s, kid, &cenc_pssh); + ngx_rtmp_cenc_content_protection_pssh(s, kid, &cenc_pssh); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, kid[0], kid[1], kid[2], kid[3], @@ -468,6 +471,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ngx_str_t *arg; ngx_rtmp_playlist_t v; + ngx_rtmp_cenc_drm_info_t drmi; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH]; @@ -475,7 +479,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; static u_char seg_path[NGX_MAX_PATH + 1]; - static u_char kid[NGX_RTMP_CENC_KEY_SIZE]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -486,7 +489,8 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) } if (dacf->cenc) { - if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { + // fill drm info + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, drmi.kid) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc kid is invalid"); return NGX_ERROR; @@ -639,7 +643,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); n = ngx_write_fd(fd, buffer, p - buffer); @@ -702,7 +706,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, @@ -800,13 +804,13 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_dash_app_conf_t *dacf; ngx_rtmp_playlist_t v; + ngx_rtmp_cenc_drm_info_t drmi; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char publish_time[NGX_RTMP_DASH_GMT_LENGTH]; static u_char buffer_depth[sizeof("P00Y00M00DT00H00M00.000S")]; static u_char frame_rate[(NGX_INT_T_LEN * 2) + 2]; - static u_char kid[NGX_RTMP_CENC_KEY_SIZE]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -817,7 +821,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (dacf->cenc) { - if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, kid) == NGX_ERROR) { + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, drmi.kid) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc kid is invalid"); return NGX_ERROR; @@ -825,7 +829,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (ctx->id == 0) { - ngx_rtmp_dash_write_init_segments(s, kid); + ngx_rtmp_dash_write_init_segments(s, &drmi); } fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, @@ -986,7 +990,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, @@ -1017,7 +1021,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - p = ngx_rtmp_dash_write_content_protection(s, dacf, kid, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, @@ -1110,7 +1114,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) static ngx_int_t -ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, u_char *kid) +ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, ngx_rtmp_cenc_drm_info_t *drmi) { ngx_fd_t fd; ngx_int_t rc; @@ -1148,7 +1152,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, u_char *kid) ngx_rtmp_mp4_write_ftyp(&b); if (dacf->cenc) { - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EVIDEO_TRACK, kid); + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EVIDEO_TRACK, drmi); } else { ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_VIDEO_TRACK, NULL); } @@ -1178,7 +1182,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s, u_char *kid) ngx_rtmp_mp4_write_ftyp(&b); if (dacf->cenc) { - ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EAUDIO_TRACK, kid); + ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_EAUDIO_TRACK, drmi); } else { ngx_rtmp_mp4_write_moov(s, &b, NGX_RTMP_MP4_AUDIO_TRACK, NULL); } diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index bb5b5358a..2370e47b3 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -537,7 +537,8 @@ ngx_rtmp_mp4_write_schm(ngx_buf_t *b) static ngx_int_t -ngx_rtmp_mp4_write_pssh_ck(ngx_buf_t *b, u_char *kid) +ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b, + ngx_rtmp_cenc_drm_info_t *drminfo) { u_char *pos; u_char ck_sid[] = { @@ -557,7 +558,7 @@ ngx_rtmp_mp4_write_pssh_ck(ngx_buf_t *b, u_char *kid) ngx_rtmp_mp4_field_32(b, 1); /* default KID */ - ngx_rtmp_mp4_data(b, kid, NGX_RTMP_CENC_KEY_SIZE); + ngx_rtmp_mp4_data(b, drminfo->kid, NGX_RTMP_CENC_KEY_SIZE); /* data size */ ngx_rtmp_mp4_field_32(b, 0); @@ -569,7 +570,7 @@ ngx_rtmp_mp4_write_pssh_ck(ngx_buf_t *b, u_char *kid) static ngx_int_t -ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, u_char *kid) +ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; @@ -589,7 +590,7 @@ ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, u_char *kid) ngx_rtmp_mp4_field_8(b, NGX_RTMP_CENC_IV_SIZE); /* default KID */ - ngx_rtmp_mp4_data(b, kid, NGX_RTMP_CENC_KEY_SIZE); + ngx_rtmp_mp4_data(b, drmi->kid, NGX_RTMP_CENC_KEY_SIZE); ngx_rtmp_mp4_update_box_size(b, pos); @@ -598,13 +599,13 @@ ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, u_char *kid) static ngx_int_t -ngx_rtmp_mp4_write_schi(ngx_buf_t *b, u_char *kid) +ngx_rtmp_mp4_write_schi(ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "schi"); - ngx_rtmp_mp4_write_tenc(b, kid); + ngx_rtmp_mp4_write_tenc(b, drmi); ngx_rtmp_mp4_update_box_size(b, pos); @@ -614,7 +615,7 @@ ngx_rtmp_mp4_write_schi(ngx_buf_t *b, u_char *kid) static ngx_int_t ngx_rtmp_mp4_write_sinf(ngx_buf_t *b, - const char format[4], u_char *kid) + const char format[4], ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; @@ -622,7 +623,7 @@ ngx_rtmp_mp4_write_sinf(ngx_buf_t *b, ngx_rtmp_mp4_write_frma(b, format); ngx_rtmp_mp4_write_schm(b); - ngx_rtmp_mp4_write_schi(b, kid); + ngx_rtmp_mp4_write_schi(b, drmi); ngx_rtmp_mp4_update_box_size(b, pos); @@ -740,7 +741,7 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_evideo(ngx_rtmp_session_t *s, - ngx_buf_t *b, u_char *kid) + ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -795,7 +796,7 @@ ngx_rtmp_mp4_write_evideo(ngx_rtmp_session_t *s, ngx_rtmp_mp4_write_avcc(s, b); - ngx_rtmp_mp4_write_sinf(b, "avc1", kid); + ngx_rtmp_mp4_write_sinf(b, "avc1", drmi); ngx_rtmp_mp4_update_box_size(b, pos); @@ -935,7 +936,7 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, - ngx_buf_t *b, u_char *kid) + ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -979,7 +980,7 @@ ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, ngx_rtmp_mp4_field_32(b, 0); #endif - ngx_rtmp_mp4_write_sinf(b, "mp4a", kid); + ngx_rtmp_mp4_write_sinf(b, "mp4a", drmi); ngx_rtmp_mp4_update_box_size(b, pos); @@ -989,7 +990,7 @@ ngx_rtmp_mp4_write_eaudio(ngx_rtmp_session_t *s, static ngx_int_t ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype, u_char *kid) + ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; @@ -1004,11 +1005,11 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, if (ttype == NGX_RTMP_MP4_VIDEO_TRACK) { ngx_rtmp_mp4_write_video(s, b); } else if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK){ - ngx_rtmp_mp4_write_evideo(s, b, kid); + ngx_rtmp_mp4_write_evideo(s, b, drmi); } else if (ttype == NGX_RTMP_MP4_AUDIO_TRACK){ ngx_rtmp_mp4_write_audio(s, b); } else if (ttype == NGX_RTMP_MP4_EAUDIO_TRACK){ - ngx_rtmp_mp4_write_eaudio(s, b, kid); + ngx_rtmp_mp4_write_eaudio(s, b, drmi); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -1084,13 +1085,13 @@ ngx_rtmp_mp4_write_stco(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype, u_char *kid) + ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "stbl"); - ngx_rtmp_mp4_write_stsd(s, b, ttype, kid); + ngx_rtmp_mp4_write_stsd(s, b, ttype, drmi); ngx_rtmp_mp4_write_stts(b); ngx_rtmp_mp4_write_stsc(b); ngx_rtmp_mp4_write_stsz(b); @@ -1104,7 +1105,7 @@ ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype, u_char *kid) + ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; @@ -1118,7 +1119,7 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, } ngx_rtmp_mp4_write_dinf(b); - ngx_rtmp_mp4_write_stbl(s, b, ttype, kid); + ngx_rtmp_mp4_write_stbl(s, b, ttype, drmi); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1128,7 +1129,7 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype, u_char *kid) + ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; @@ -1136,7 +1137,7 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_write_mdhd(b); ngx_rtmp_mp4_write_hdlr(b, ttype); - ngx_rtmp_mp4_write_minf(s, b, ttype, kid); + ngx_rtmp_mp4_write_minf(s, b, ttype, drmi); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1145,14 +1146,14 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype, u_char *kid) + ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; pos = ngx_rtmp_mp4_start_box(b, "trak"); ngx_rtmp_mp4_write_tkhd(s, b, ttype); - ngx_rtmp_mp4_write_mdia(s, b, ttype, kid); + ngx_rtmp_mp4_write_mdia(s, b, ttype, drmi); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1197,7 +1198,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b) ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype, u_char *kid) + ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; @@ -1205,11 +1206,11 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_write_mvhd(b); ngx_rtmp_mp4_write_mvex(b); - ngx_rtmp_mp4_write_trak(s, b, ttype, kid); + ngx_rtmp_mp4_write_trak(s, b, ttype, drmi); if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK || ttype == NGX_RTMP_MP4_EAUDIO_TRACK) { - ngx_rtmp_mp4_write_pssh_ck(b, kid); + ngx_rtmp_mp4_write_pssh_cenc(b, drmi); } ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 5161767c1..455cd1040 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -45,7 +45,7 @@ typedef enum { ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b); ngx_int_t ngx_rtmp_mp4_write_styp(ngx_buf_t *b); ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_track_type_t ttype, u_char *kid); + ngx_rtmp_mp4_track_type_t ttype, ngx_rtmp_cenc_drm_info_t *drmi); ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, char type, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, uint32_t index, ngx_flag_t is_protected); From 2ab001d3e0de3f878f1082005c07dbc126c6f597 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 7 Jun 2018 18:38:06 +0000 Subject: [PATCH 092/113] Better but good enough --- dash/ngx_rtmp_dash_module.c | 39 +++++++++++++++---------------------- 1 file changed, 16 insertions(+), 23 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 1c93049f9..138b08b91 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -100,6 +100,8 @@ typedef struct { ngx_rtmp_dash_track_t video; ngx_rtmp_dash_variant_t *var; + ngx_rtmp_cenc_drm_info_t drm_info; + } ngx_rtmp_dash_ctx_t; @@ -423,18 +425,18 @@ static u_char * ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, ngx_rtmp_dash_app_conf_t *dacf, ngx_rtmp_cenc_drm_info_t *drmi, u_char *p, u_char *last) { - u_char *kid; + u_char *k; ngx_str_t cenc_pssh; - kid = drmi->kid; + k = drmi->kid; if (dacf->cenc) { - ngx_rtmp_cenc_content_protection_pssh(s, kid, &cenc_pssh); + ngx_rtmp_cenc_content_protection_pssh(s, k, &cenc_pssh); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, - kid[0], kid[1], kid[2], kid[3], - kid[4], kid[5], kid[6], kid[7], - kid[8], kid[9], kid[10], kid[11], kid[12], kid[13], kid[14], kid[15]); + k[0], k[1], k[2], k[3], + k[4], k[5], k[6], k[7], + k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15]); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, &cenc_pssh); @@ -471,7 +473,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) ngx_str_t *arg; ngx_rtmp_playlist_t v; - ngx_rtmp_cenc_drm_info_t drmi; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH]; @@ -488,15 +489,6 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } - if (dacf->cenc) { - // fill drm info - if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, drmi.kid) == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: error cenc kid is invalid"); - return NGX_ERROR; - } - } - fd = ngx_open_file(ctx->var_playlist_bak.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -643,7 +635,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); n = ngx_write_fd(fd, buffer, p - buffer); @@ -706,7 +698,7 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, @@ -804,7 +796,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_dash_app_conf_t *dacf; ngx_rtmp_playlist_t v; - ngx_rtmp_cenc_drm_info_t drmi; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; static u_char available_time[NGX_RTMP_DASH_GMT_LENGTH]; @@ -821,7 +812,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (dacf->cenc) { - if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, drmi.kid) == NGX_ERROR) { + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, ctx->drm_info.kid) == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc kid is invalid"); return NGX_ERROR; @@ -829,7 +820,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) } if (ctx->id == 0) { - ngx_rtmp_dash_write_init_segments(s, &drmi); + ngx_rtmp_dash_write_init_segments(s, &ctx->drm_info); } fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, @@ -990,7 +981,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, @@ -1021,7 +1012,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - p = ngx_rtmp_dash_write_content_protection(s, dacf, &drmi, p, last); + p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, @@ -1761,6 +1752,8 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) { return NGX_ERROR; } + + //drm_info ? next: return next_publish(s, v); From ee32286455a9c3bd754d2dcbc3c91012310c8b28 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 7 Jun 2018 20:10:17 +0000 Subject: [PATCH 093/113] Better --- dash/ngx_rtmp_cenc.h | 4 +-- dash/ngx_rtmp_dash_module.c | 60 +++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 28 deletions(-) diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index eb62405fe..c1f55719e 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -9,9 +9,9 @@ typedef struct { unsigned wdv:1; - ngx_str_t wdv_pssh; + ngx_str_t wdv_data; unsigned mspr:1; - ngx_str_t mspr_pssh; + ngx_str_t mspr_data; u_char kid[NGX_RTMP_CENC_KEY_SIZE]; } ngx_rtmp_cenc_drm_info_t; diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 138b08b91..9b4cd7594 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -422,7 +422,7 @@ ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t static u_char * -ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, ngx_rtmp_dash_app_conf_t *dacf, +ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, ngx_rtmp_cenc_drm_info_t *drmi, u_char *p, u_char *last) { u_char *k; @@ -430,21 +430,19 @@ ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, ngx_rtmp_dash_app_ k = drmi->kid; - if (dacf->cenc) { - ngx_rtmp_cenc_content_protection_pssh(s, k, &cenc_pssh); + ngx_rtmp_cenc_content_protection_pssh(s, k, &cenc_pssh); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, - k[0], k[1], k[2], k[3], - k[4], k[5], k[6], k[7], - k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, + k[0], k[1], k[2], k[3], + k[4], k[5], k[6], k[7], + k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15]); - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, - &cenc_pssh); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, + &cenc_pssh); - if (dacf->wdv) { - p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, - &dacf->wdv_data); - } + if (drmi->wdv) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_WDV, + &drmi->wdv_data); } return p; @@ -635,7 +633,9 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); + if (dacf->cenc) { + p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last); + } n = ngx_write_fd(fd, buffer, p - buffer); @@ -698,7 +698,9 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); + if (dacf->cenc) { + p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last); + } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, @@ -811,14 +813,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_ERROR; } - if (dacf->cenc) { - if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, ctx->drm_info.kid) == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, - "dash: error cenc kid is invalid"); - return NGX_ERROR; - } - } - if (ctx->id == 0) { ngx_rtmp_dash_write_init_segments(s, &ctx->drm_info); } @@ -981,7 +975,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(p, last, NGX_RTMP_DASH_INBAND_EVENT); } - p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); + if (dacf->cenc) { + p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last); + } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO, &ctx->name, @@ -1012,7 +1008,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_ADAPTATIONSET_AUDIO); - p = ngx_rtmp_dash_write_content_protection(s, dacf, &ctx->drm_info, p, last); + if (dacf->cenc) { + p = ngx_rtmp_dash_write_content_protection(s, &ctx->drm_info, p, last); + } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_REPRESENTATION_AUDIO, &ctx->name, @@ -1753,7 +1751,17 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) return NGX_ERROR; } - //drm_info ? + /* drm info */ + if (dacf->cenc) { + if (ngx_rtmp_cenc_read_hex(dacf->cenc_kid, ctx->drm_info.kid) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "dash: error cenc kid is invalid"); + return NGX_ERROR; + } + if (dacf->wdv) { + ctx->drm_info.wdv = 1; + ctx->drm_info.wdv_data = dacf->wdv_data; + } + } next: return next_publish(s, v); From 17cd0df0c72674fd956844f51b5c8687cf8b3082 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sat, 9 Jun 2018 10:41:09 +0000 Subject: [PATCH 094/113] better static alloc --- dash/ngx_rtmp_cenc.c | 10 +++++----- dash/ngx_rtmp_cenc.h | 7 ++++--- dash/ngx_rtmp_dash_module.c | 2 +- dash/ngx_rtmp_mp4.c | 35 +++++++++++++++++++++++++++++++++-- 4 files changed, 43 insertions(+), 11 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index 3dba47e62..f0b81e757 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -141,7 +141,7 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *i size_t crypted_data_len; /* small sample : leave it in clear */ - if (data_len <= NGX_RTMP_CENC_MIN_CLEAR) { + if (data_len <= NGX_RTMP_CENC_MIN_CLEAR_SIZE) { *clear_data_len = data_len; return NGX_OK; } @@ -149,7 +149,7 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *i /* skip sufficient amount of data to leave nalu header/infos * in clear to conform to the norm */ crypted_data_len = - ((data_len - NGX_RTMP_CENC_MIN_CLEAR) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; + ((data_len - NGX_RTMP_CENC_MIN_CLEAR_SIZE) / AES_BLOCK_SIZE) * AES_BLOCK_SIZE; *clear_data_len = data_len - crypted_data_len; data += *clear_data_len; @@ -159,10 +159,10 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, uint8_t *key, uint8_t *i ngx_int_t -ngx_rtmp_cenc_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, - ngx_str_t *dest_pssh) +ngx_rtmp_cenc_content_protection_pssh(u_char* kid, ngx_str_t *dest_pssh) { ngx_str_t src_pssh; + u_char dest[NGX_RTMP_CENC_MAX_PSSH_SIZE]; u_char pssh[] = { 0x00, 0x00, 0x00, 0x34, 0x70, 0x73, 0x73, 0x68, // pssh box header @@ -181,7 +181,7 @@ ngx_rtmp_cenc_content_protection_pssh(ngx_rtmp_session_t *s, u_char* kid, src_pssh.data = pssh; dest_pssh->len = ngx_base64_encoded_length(src_pssh.len); - dest_pssh->data = ngx_palloc(s->connection->pool, dest_pssh->len); + dest_pssh->data = dest; ngx_encode_base64(dest_pssh, &src_pssh); diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index c1f55719e..08036b0e6 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -4,7 +4,8 @@ #define NGX_RTMP_CENC_IV_SIZE (8) #define NGX_RTMP_CENC_KEY_SIZE (16) -#define NGX_RTMP_CENC_MIN_CLEAR (100) +#define NGX_RTMP_CENC_MIN_CLEAR_SIZE (100) +#define NGX_RTMP_CENC_MAX_PSSH_SIZE (1024) typedef struct { @@ -35,7 +36,7 @@ ngx_rtmp_cenc_encrypt_sub_sample(ngx_rtmp_session_t *s, size_t data_len, size_t *clear_data_len); ngx_int_t -ngx_rtmp_cenc_content_protection_pssh(ngx_rtmp_session_t *s, - u_char* kid, ngx_str_t *dest_pssh); +ngx_rtmp_cenc_content_protection_pssh(u_char* kid, + ngx_str_t *dest_pssh); #endif /* _NGX_RTMP_CENC_H_INCLUDED_ */ diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 9b4cd7594..109fd064f 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -430,7 +430,7 @@ ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, k = drmi->kid; - ngx_rtmp_cenc_content_protection_pssh(s, k, &cenc_pssh); + ngx_rtmp_cenc_content_protection_pssh(k, &cenc_pssh); p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_CENC, k[0], k[1], k[2], k[3], diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 2370e47b3..065e44232 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -541,7 +541,7 @@ ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drminfo) { u_char *pos; - u_char ck_sid[] = { + u_char sid[] = { 0x10, 0x77, 0xef, 0xec, 0xc0, 0xb2, 0x4d, 0x02, 0xac, 0xe3, 0x3c, 0x1e, 0x52, 0xe2, 0xfb, 0x4b }; @@ -552,7 +552,7 @@ ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b, ngx_rtmp_mp4_field_32(b, 0x01000000); /* system ID : org.w3.clearkey */ - ngx_rtmp_mp4_data(b, ck_sid, NGX_RTMP_CENC_KEY_SIZE); + ngx_rtmp_mp4_data(b, sid, NGX_RTMP_CENC_KEY_SIZE); /* kid count */ ngx_rtmp_mp4_field_32(b, 1); @@ -569,6 +569,34 @@ ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b, } +static ngx_int_t +ngx_rtmp_mp4_write_pssh_wdv(ngx_buf_t *b, + ngx_rtmp_cenc_drm_info_t *drminfo) +{ + u_char *pos; + u_char sid[] = { + 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, + 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed + }; + + pos = ngx_rtmp_mp4_start_box(b, "pssh"); + + /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0); + + /* system ID : com.widevine.alpha */ + ngx_rtmp_mp4_data(b, sid, NGX_RTMP_CENC_KEY_SIZE); + + /* decode base64 wdv_data */ + /* data size */ + ngx_rtmp_mp4_field_32(b, 0); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi) { @@ -1211,6 +1239,9 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, if (ttype == NGX_RTMP_MP4_EVIDEO_TRACK || ttype == NGX_RTMP_MP4_EAUDIO_TRACK) { ngx_rtmp_mp4_write_pssh_cenc(b, drmi); + if (drmi->wdv) { + ngx_rtmp_mp4_write_pssh_wdv(b, drmi); + } } ngx_rtmp_mp4_update_box_size(b, pos); From 1444985cd4bab3e36edbf3301ee314732c7b3946 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sat, 9 Jun 2018 14:58:41 +0000 Subject: [PATCH 095/113] Ok go for wdv pssh in init segment --- dash/ngx_rtmp_dash_module.c | 1 + dash/ngx_rtmp_mp4.c | 22 ++++++++++++++++------ 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 109fd064f..c9adf34a8 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -437,6 +437,7 @@ ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, k[4], k[5], k[6], k[7], k[8], k[9], k[10], k[11], k[12], k[13], k[14], k[15]); + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_CENC, &cenc_pssh); diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 065e44232..723bd495a 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -538,7 +538,7 @@ ngx_rtmp_mp4_write_schm(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b, - ngx_rtmp_cenc_drm_info_t *drminfo) + ngx_rtmp_cenc_drm_info_t *drmi) { u_char *pos; u_char sid[] = { @@ -558,7 +558,7 @@ ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b, ngx_rtmp_mp4_field_32(b, 1); /* default KID */ - ngx_rtmp_mp4_data(b, drminfo->kid, NGX_RTMP_CENC_KEY_SIZE); + ngx_rtmp_mp4_data(b, drmi->kid, NGX_RTMP_CENC_KEY_SIZE); /* data size */ ngx_rtmp_mp4_field_32(b, 0); @@ -571,10 +571,13 @@ ngx_rtmp_mp4_write_pssh_cenc(ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_pssh_wdv(ngx_buf_t *b, - ngx_rtmp_cenc_drm_info_t *drminfo) + ngx_rtmp_cenc_drm_info_t *drmi) { - u_char *pos; - u_char sid[] = { + ngx_str_t dest, src; + u_char *pos; + u_char buf[NGX_RTMP_CENC_MAX_PSSH_SIZE]; + + u_char sid[] = { 0xed, 0xef, 0x8b, 0xa9, 0x79, 0xd6, 0x4a, 0xce, 0xa3, 0xc8, 0x27, 0xdc, 0xd5, 0x1d, 0x21, 0xed }; @@ -588,8 +591,15 @@ ngx_rtmp_mp4_write_pssh_wdv(ngx_buf_t *b, ngx_rtmp_mp4_data(b, sid, NGX_RTMP_CENC_KEY_SIZE); /* decode base64 wdv_data */ + dest.data = buf; + src.len = ngx_base64_decoded_length(drmi->wdv_data.len) - 32; + ngx_decode_base64(&dest, &drmi->wdv_data); + /* data size */ - ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, src.len); + + /* data */ + ngx_rtmp_mp4_data(b, dest.data + 32, src.len); ngx_rtmp_mp4_update_box_size(b, pos); From c4b5213c7d15e91e3d2250d7be79ac1e5c414257 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sat, 9 Jun 2018 14:59:59 +0000 Subject: [PATCH 096/113] Clarification --- dash/ngx_rtmp_mp4.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 723bd495a..c8e37d580 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -584,7 +584,7 @@ ngx_rtmp_mp4_write_pssh_wdv(ngx_buf_t *b, pos = ngx_rtmp_mp4_start_box(b, "pssh"); - /* version and flags */ + /* assuming v0 pssh for widevine */ ngx_rtmp_mp4_field_32(b, 0); /* system ID : com.widevine.alpha */ From 6006e831391f2e64f4144efb34750805cc67244a Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Sat, 9 Jun 2018 16:53:02 +0000 Subject: [PATCH 097/113] MSPR first try --- TODO | 10 +++---- dash/ngx_rtmp_cenc.h | 4 ++- dash/ngx_rtmp_dash_module.c | 50 ++++++++++++++++++++++++++++++++++ dash/ngx_rtmp_dash_templates.h | 14 ++++++++++ 4 files changed, 72 insertions(+), 6 deletions(-) diff --git a/TODO b/TODO index c81e96eec..026624cfc 100644 --- a/TODO +++ b/TODO @@ -1,10 +1,10 @@ - doc -- need re-upstream +- need re-upstream // wait response - rewritte the variant code for dash, using memory ? - test common encryption code (OK clearkey, Widevine both on chrome/firefox!) - need to write some doc about cenc/drm implem - - refacto code for writing content protection in manifest // SEMI OK + - refacto code for writing content protection in manifest // OK - clarify the use of %V in ngx_printf // OK - refacto code for init / kid // OK - correct pssh in manifest // OK @@ -12,7 +12,7 @@ - add real base64 encrypt pssh data // OK - add support for sub sample encryption //OK - add sig for wdv //OK - - add struct for drm info // in progress - - add pssh in init file for wdv - - add msplayready support + - add struct for drm info // OK + - add pssh in init file for wdv // OK + - add msplayready support // WIP diff --git a/dash/ngx_rtmp_cenc.h b/dash/ngx_rtmp_cenc.h index 08036b0e6..a04c002e7 100644 --- a/dash/ngx_rtmp_cenc.h +++ b/dash/ngx_rtmp_cenc.h @@ -9,11 +9,13 @@ typedef struct { + u_char kid[NGX_RTMP_CENC_KEY_SIZE]; unsigned wdv:1; ngx_str_t wdv_data; unsigned mspr:1; ngx_str_t mspr_data; - u_char kid[NGX_RTMP_CENC_KEY_SIZE]; + ngx_str_t mspr_kid; + ngx_str_t mspr_pro; } ngx_rtmp_cenc_drm_info_t; diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index c9adf34a8..f98a3a2e7 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -145,6 +145,10 @@ typedef struct { ngx_str_t cenc_kid; ngx_flag_t wdv; ngx_str_t wdv_data; + ngx_flag_t mspr; + ngx_str_t mspr_data; + ngx_str_t mspr_kid; + ngx_str_t mspr_pro; ngx_flag_t repetition; ngx_uint_t clock_compensation; // Try to compensate clock drift // between client and server (on client side) @@ -246,6 +250,34 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, wdv_data), NULL }, + { ngx_string("dash_mspr"), + 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_dash_app_conf_t, mspr), + NULL }, + + { ngx_string("dash_mspr_data"), + 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_dash_app_conf_t, mspr_data), + NULL }, + + { ngx_string("dash_mspr_kid"), + 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_dash_app_conf_t, mspr_kid), + NULL }, + + { ngx_string("dash_mspr_pro"), + 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_dash_app_conf_t, mspr_pro), + NULL }, + { ngx_string("dash_clock_compensation"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -446,6 +478,13 @@ ngx_rtmp_dash_write_content_protection(ngx_rtmp_session_t *s, &drmi->wdv_data); } + if (drmi->mspr) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_MSPR, + &drmi->mspr_data, + &drmi->mspr_kid, + &drmi->mspr_pro); + } + return p; } @@ -1762,6 +1801,12 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ctx->drm_info.wdv = 1; ctx->drm_info.wdv_data = dacf->wdv_data; } + if (dacf->mspr) { + ctx->drm_info.mspr = 1; + ctx->drm_info.mspr_data = dacf->mspr_data; + ctx->drm_info.mspr_kid = dacf->mspr_kid; + ctx->drm_info.mspr_pro = dacf->mspr_pro; + } } next: @@ -2663,6 +2708,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) conf->repetition = NGX_CONF_UNSET; conf->cenc = NGX_CONF_UNSET; conf->wdv = NGX_CONF_UNSET; + conf->mspr = NGX_CONF_UNSET; conf->clock_compensation = NGX_CONF_UNSET; conf->ad_markers = NGX_CONF_UNSET; @@ -2688,6 +2734,10 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->cenc_kid, prev->cenc_kid, ""); ngx_conf_merge_value(conf->wdv, prev->wdv, 0); ngx_conf_merge_str_value(conf->wdv_data, prev->wdv_data, ""); + ngx_conf_merge_value(conf->mspr, prev->mspr, 0); + ngx_conf_merge_str_value(conf->mspr_data, prev->mspr_data, ""); + ngx_conf_merge_str_value(conf->mspr_kid, prev->mspr_kid, ""); + ngx_conf_merge_str_value(conf->mspr_pro, prev->mspr_pro, ""); ngx_conf_merge_uint_value(conf->clock_compensation, prev->clock_compensation, NGX_RTMP_DASH_CLOCK_COMPENSATION_OFF); ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index 8adb77311..cad90d466 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -8,6 +8,7 @@ " type=\"dynamic\"\n" \ " xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" \ " xmlns:cenc=\"urn:mpeg:cenc:2013\"\n" \ + " xmlns:mspr=\"urn:microsoft:playready\"\n" \ " availabilityStartTime=\"%s\"\n" \ " publishTime=\"%s\"\n" \ " minimumUpdatePeriod=\"PT%ui.%03uiS\"\n" \ @@ -66,6 +67,19 @@ " \n" +#define NGX_RTMP_DASH_MANIFEST_CONTENT_PROTECTION_PSSH_MSPR \ + " \n" \ + " %V\n" \ + " 1\n" \ + " 8\n" \ + " %V\n" \ + " %V\n" \ + " \n" + + #define NGX_RTMP_DASH_MANIFEST_REPRESENTATION_VIDEO \ " Date: Mon, 9 Jul 2018 09:14:56 +0000 Subject: [PATCH 098/113] Up2date todo --- TODO | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO b/TODO index 026624cfc..66a440077 100644 --- a/TODO +++ b/TODO @@ -14,5 +14,5 @@ - add sig for wdv //OK - add struct for drm info // OK - add pssh in init file for wdv // OK - - add msplayready support // WIP + - add msplayready support // OK need pssh in init file ? From 5fc75736a14af3eb64873f5ca677aaa5d31a8377 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Mon, 9 Jul 2018 16:28:03 +0000 Subject: [PATCH 099/113] Add mspr in init --- dash/ngx_rtmp_mp4.c | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index c8e37d580..e1cf0fbc4 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -607,6 +607,45 @@ ngx_rtmp_mp4_write_pssh_wdv(ngx_buf_t *b, } +static ngx_int_t +ngx_rtmp_mp4_write_pssh_mspr(ngx_buf_t *b, + ngx_rtmp_cenc_drm_info_t *drmi) +{ + ngx_str_t dest, src; + u_char *pos; + u_char buf[NGX_RTMP_CENC_MAX_PSSH_SIZE]; + + u_char sid[] = { + 0x9a, 0x04, 0xf0, 0x79, 0x98, 0x40, 0x42, 0x86, + 0xab, 0x92, 0xe6, 0x5b, 0xe0, 0x88, 0x5f, 0x95 + }; + + pos = ngx_rtmp_mp4_start_box(b, "pssh"); + + /* assuming v0 pssh for playready */ + ngx_rtmp_mp4_field_32(b, 0); + + /* system ID : com.microsoft.playready */ + ngx_rtmp_mp4_data(b, sid, NGX_RTMP_CENC_KEY_SIZE); + + /* decode base64 mspr_data */ + dest.data = buf; + src.len = ngx_base64_decoded_length(drmi->mspr_data.len) - 32; + ngx_decode_base64(&dest, &drmi->mspr_data); + + /* data size */ + ngx_rtmp_mp4_field_32(b, src.len); + + /* data */ + ngx_rtmp_mp4_data(b, dest.data + 32, src.len); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + + + static ngx_int_t ngx_rtmp_mp4_write_tenc(ngx_buf_t *b, ngx_rtmp_cenc_drm_info_t *drmi) { @@ -1252,6 +1291,9 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, if (drmi->wdv) { ngx_rtmp_mp4_write_pssh_wdv(b, drmi); } + if (drmi->mspr) { + ngx_rtmp_mp4_write_pssh_mspr(b, drmi); + } } ngx_rtmp_mp4_update_box_size(b, pos); From 089921ed2a97f40fca6e9c6a95e1592b2d2396d2 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Tue, 10 Jul 2018 08:55:28 +0000 Subject: [PATCH 100/113] New readme --- README.md | 48 ++++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index ec5f95543..77ef9cabe 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,19 @@ ## nginx-rtmp-module (dash enhanced version) -Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version. +Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to date version (until now) - - add the possibility to have dash variant like in HLS (show below configuration, using ffmpeg to trancode in 3 variants). +Notable new features : + + - add the possibility to have dash variant like (show below configuration, using ffmpeg to trancode in 3 variants). note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest. + you can also use any encoder to directly push the variant. + - add the support of using repetition in manifest to shorten them (optopn dash_repetition) + - add the support of common-encryption; currently working DRM are ClearKey/Widevine/Playready (see specific doc Here) - add the support of inband scte event, from rtmp AMF event to dash (InbandEvent in manifest and emsg box in mp4 fragment) - - add the support of using repetition in manifest to shorten them - - add the support of common-encryption; currently working DRM are ClearKey and Widevine (see specific doc Here) + + +See original doc here for full list of options. ``` rtmp { @@ -29,10 +35,11 @@ Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to dat application dash { live on; dash on; - dash_nested on; # this work but not separate the variant mpd + dash_nested on; + dash_repetition on; dash_path /dev/shm/dash; - dash_fragment 2; # 2 second is generaly a good choice for live - dash_playlist_length 120; # keep 240s of tail + dash_fragment 4; # 4 second is generaly a good choice for live + dash_playlist_length 120; # keep 120s of tail dash_cleanup on; dash_variant _low bandwidth="256000" width="320" height="180"; dash_variant _med bandwidth="832000" width="640" height="360"; @@ -40,17 +47,30 @@ Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to dat } } - http { - server { - listen 80; - - location /dash { - alias /dev/shm/dash; + server { + listen 443 ssl; + location / { + root /var/www; add_header Cache-Control no-cache; add_header 'Access-Control-Allow-Origin' '*'; } + location /dash/live/index.mpd { + alias /dev/shm/dash/live/index.mpd; + add_header 'Access-Control-Allow-Origin' '*'; + add_header Cache-Control 'public, max-age=0, s-maxage=2'; + } + location /dash/live { + alias /dev/shm/dash/live; + add_header 'Access-Control-Allow-Origin' '*'; + add_header Cache-Control 'public, max-age=600, s-maxage=600'; + } + + server_name live.site.net; + ssl_certificate /etc/letsencrypt/live/live.site.net/fullchain.pem; + ssl_certificate_key /etc/letsencrypt/live/live.sit.net/privkey.pem; + } - } +} ``` From c7c96e35c13b304fde0f845cf85b45d882ca9018 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 12 Jul 2018 09:33:33 +0000 Subject: [PATCH 101/113] Add DRM file :) --- DRM | 74 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 DRM diff --git a/DRM b/DRM new file mode 100644 index 000000000..c0c47c43c --- /dev/null +++ b/DRM @@ -0,0 +1,74 @@ + +DRM. + +This fork give the possibility of packaging dash "protected" stream. +Concretely it implement the minimal requirement of "common-encryption" as described in ISO/IEC 23001-7:2015, Information technology — MPEG systems technologies — Part 7: Common encryption in ISO Base Media File Format files - 2nd Edition. +You can read a brief description here : "https://w3c.github.io/encrypted-media/format-registry/stream/mp4.html#bib-CENC" + +How to use it : + +You need at least to enable common_encryption and provide one key and one key id with the following directives : + +``` +dash_cenc on; # enable common encryption on all stream in this block +dash_cenc_kid XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; # 16 bytes KEY-ID in hex +dash_cenc_key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX; # 16 bytes KEY in hex +``` + +It enable automatically Clear-Key pseudo DRM. (use it only for testing purpose) + +Currently the are two real DRM supported : Widevine and Microsoft Playready. + +For widevine you need the following directives in addition : + +``` +dash_wdv on; # enable widevine signalling +dash_wdv_data AAAAbHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAA... ; # base64 encoded widevine pssh +``` + +For playready you need the following directives in addition (you can use both widevine and playready together with the same kid:key pair): + +``` +dash_mspr on; # enable playready signalling +dash_mspr_data AAACsHBzc2gAAAAAmgTweZhAQoarkuZb4Ih...; # base64 encoded playready pssh +dash_mspr_kid AAATH/7xxxfUbpB8mhqA==; # base64 encoded playready kid +dash_mspr_pro kAIAAAEAAQCGAjwAVwBSAE0ASABFAEEARA...; # base64 encoded playready PRO (Playready Object) +``` + +Implementation : + +TLDR; This was quite an adventure + +The implementation is based on the ISO_IEC_23001-7_2016 normative document. +I also took lot of inspiration on kaltura nginx-vod module. + +It implement the minimal requirement of the norm, the 'cenc' scheme, AES-CTR mode full sample and video NAL Subsample encryption. +Audio track are encrypted in full sample mode with AES-CTR. +Video track are encrypted in sub sample mode, assuming one NALU per frame, using enough clear text size at the beginning of the frame to keep the NAL header in clear. (the module does not analyse NAL Headers). +The clear size is rounded to make encrypted size of data a multiple of the AES-CRT block size. +The implementation allow only one KID:KEY couple used for all tracks. +The implementation use 64bits IVs. + +Conformity : + +This implementation have been tested with and known working : + +Clearkey : + - Firefox: dashjs/shakaplayer + - Chrome: dashjs/shakaplayer + +Widevine : +- Firefox: dashjs/shakaplayer +- Chrome: dashjs/shakaplayer + +Playready: + - Edge : dashjs/shakaplayer + +Bitmovin player seem also to work. + +Thanks: + +Special thanks to Eran Kornblau from Kaltura, Joey Parrish, and Jacob Timble. + + + From 7db1076d3e182d132c19cecc568ef86213f1e446 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 12 Jul 2018 09:42:07 +0000 Subject: [PATCH 102/113] md file --- DRM => DRM.md | 10 +++++----- TODO | 18 ------------------ TODO.md | 20 ++++++++++++++++++++ 3 files changed, 25 insertions(+), 23 deletions(-) rename DRM => DRM.md (95%) delete mode 100644 TODO create mode 100644 TODO.md diff --git a/DRM b/DRM.md similarity index 95% rename from DRM rename to DRM.md index c0c47c43c..ffa7c561e 100644 --- a/DRM +++ b/DRM.md @@ -1,11 +1,11 @@ -DRM. +## DRM Common Encryption This fork give the possibility of packaging dash "protected" stream. Concretely it implement the minimal requirement of "common-encryption" as described in ISO/IEC 23001-7:2015, Information technology — MPEG systems technologies — Part 7: Common encryption in ISO Base Media File Format files - 2nd Edition. You can read a brief description here : "https://w3c.github.io/encrypted-media/format-registry/stream/mp4.html#bib-CENC" -How to use it : +### How to use it : You need at least to enable common_encryption and provide one key and one key id with the following directives : @@ -35,9 +35,9 @@ dash_mspr_kid AAATH/7xxxfUbpB8mhqA==; # base64 encoded playready kid dash_mspr_pro kAIAAAEAAQCGAjwAVwBSAE0ASABFAEEARA...; # base64 encoded playready PRO (Playready Object) ``` -Implementation : +### Implementation : -TLDR; This was quite an adventure +_TLDR;_ This was quite an adventure The implementation is based on the ISO_IEC_23001-7_2016 normative document. I also took lot of inspiration on kaltura nginx-vod module. @@ -49,7 +49,7 @@ The clear size is rounded to make encrypted size of data a multiple of the AES-C The implementation allow only one KID:KEY couple used for all tracks. The implementation use 64bits IVs. -Conformity : +### Conformity : This implementation have been tested with and known working : diff --git a/TODO b/TODO deleted file mode 100644 index 66a440077..000000000 --- a/TODO +++ /dev/null @@ -1,18 +0,0 @@ -- doc -- need re-upstream // wait response -- rewritte the variant code for dash, using memory ? - -- test common encryption code (OK clearkey, Widevine both on chrome/firefox!) - - need to write some doc about cenc/drm implem - - refacto code for writing content protection in manifest // OK - - clarify the use of %V in ngx_printf // OK - - refacto code for init / kid // OK - - correct pssh in manifest // OK - - add pssh / cenc in variant mpd // OK - - add real base64 encrypt pssh data // OK - - add support for sub sample encryption //OK - - add sig for wdv //OK - - add struct for drm info // OK - - add pssh in init file for wdv // OK - - add msplayready support // OK need pssh in init file ? - diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..fa5a5c934 --- /dev/null +++ b/TODO.md @@ -0,0 +1,20 @@ +## TODO + +- doc // OK +- need re-upstream // wait response +- rewritte the variant code for dash, using memory ? + +- test common encryption code (OK clearkey, Widevine both on chrome/firefox!) +- need to write some doc about cenc/drm implem +- refacto code for writing content protection in manifest // OK +- clarify the use of %V in ngx_printf // OK +- refacto code for init / kid // OK +- correct pssh in manifest // OK +- add pssh / cenc in variant mpd // OK +- add real base64 encrypt pssh data // OK +- add support for sub sample encryption //OK +- add sig for wdv //OK +- add struct for drm info // OK +- add pssh in init file for wdv // OK +- add msplayready support // OK need pssh in init file + From 9b4fed5dc52ba43bda32711d189fa9d3175ed055 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Thu, 12 Jul 2018 09:43:20 +0000 Subject: [PATCH 103/113] Clarity --- DRM.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/DRM.md b/DRM.md index ffa7c561e..932653a34 100644 --- a/DRM.md +++ b/DRM.md @@ -43,10 +43,15 @@ The implementation is based on the ISO_IEC_23001-7_2016 normative document. I also took lot of inspiration on kaltura nginx-vod module. It implement the minimal requirement of the norm, the 'cenc' scheme, AES-CTR mode full sample and video NAL Subsample encryption. + Audio track are encrypted in full sample mode with AES-CTR. + Video track are encrypted in sub sample mode, assuming one NALU per frame, using enough clear text size at the beginning of the frame to keep the NAL header in clear. (the module does not analyse NAL Headers). + The clear size is rounded to make encrypted size of data a multiple of the AES-CRT block size. + The implementation allow only one KID:KEY couple used for all tracks. + The implementation use 64bits IVs. ### Conformity : From 12698a924ed113385a75e14144df3aa0408d009f Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 10 Oct 2018 12:06:09 +0000 Subject: [PATCH 104/113] Add some doc and time_hack --- DRM.md | 1 + README.md | 6 ++-- TODO.md | 3 ++ dash/ngx_rtmp_dash_module.c | 59 ++++++++++++++++++++++++++++------ dash/ngx_rtmp_dash_templates.h | 2 ++ 5 files changed, 58 insertions(+), 13 deletions(-) diff --git a/DRM.md b/DRM.md index 932653a34..11797edee 100644 --- a/DRM.md +++ b/DRM.md @@ -73,6 +73,7 @@ Bitmovin player seem also to work. Thanks: +Thanks to all the opensource communautie. Special thanks to Eran Kornblau from Kaltura, Joey Parrish, and Jacob Timble. diff --git a/README.md b/README.md index 77ef9cabe..dfc9540c0 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Notable new features : - add the possibility to have dash variant like (show below configuration, using ffmpeg to trancode in 3 variants). note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest. you can also use any encoder to directly push the variant. - - add the support of using repetition in manifest to shorten them (optopn dash_repetition) - - add the support of common-encryption; currently working DRM are ClearKey/Widevine/Playready (see specific doc Here) - - add the support of inband scte event, from rtmp AMF event to dash (InbandEvent in manifest and emsg box in mp4 fragment) + - add the support of using repetition in manifest to shorten them (optoin dash_repetition) + - add the support of common-encryption; currently working DRM are ClearKey/Widevine/Playready (see specific doc [here](DRM.md)) + - add the support of ad insertion break event, from rtmp AMF message to dash (InbandEvent in manifest and emsg box in mp4 fragment, see doc [here](DAI.md)) See original doc here for full list of options. diff --git a/TODO.md b/TODO.md index fa5a5c934..6dd0b18b5 100644 --- a/TODO.md +++ b/TODO.md @@ -4,6 +4,9 @@ - need re-upstream // wait response - rewritte the variant code for dash, using memory ? +- add option emsg_timestamp_hack + + - test common encryption code (OK clearkey, Widevine both on chrome/firefox!) - need to write some doc about cenc/drm implem - refacto code for writing content protection in manifest // OK diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index f98a3a2e7..99a21360b 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -36,6 +36,7 @@ static ngx_int_t ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s); #define NGX_RTMP_DASH_GMT_LENGTH sizeof("1970-09-28T12:00:00+06:00") typedef struct { + uint64_t u_timestamp; uint32_t timestamp; uint32_t duration; } ngx_rtmp_dash_frag_t; @@ -161,6 +162,7 @@ typedef struct { ngx_path_t *slot; ngx_array_t *variant; ngx_uint_t ad_markers; + ngx_flag_t ad_markers_timehack; } ngx_rtmp_dash_app_conf_t; @@ -306,6 +308,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { offsetof(ngx_rtmp_dash_app_conf_t, ad_markers), &ngx_rtmp_dash_ad_markers_type_slots }, + { ngx_string("dash_ad_markers_timehack"), + 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_dash_app_conf_t, ad_markers_timehack), + NULL }, + ngx_null_command }; @@ -443,9 +452,8 @@ ngx_rtmp_dash_write_segment_timeline(ngx_rtmp_session_t *s, ngx_rtmp_dash_ctx_t p = ngx_rtmp_dash_write_segment(p, last, t, d, r); } } else { - t = f->timestamp; - d = f->duration; - p = ngx_rtmp_dash_write_segment(p, last, t, d, 0); + //p = ngx_rtmp_dash_write_segment(p, last, f->u_timestamp, f->duration, 0); + p = ngx_rtmp_dash_write_segment(p, last, f->timestamp, f->duration, 0); } } @@ -549,11 +557,19 @@ ngx_rtmp_dash_write_variant_playlist(ngx_rtmp_session_t *s) * Cos segments time counting from it */ ngx_libc_gmtime(start_time, &tm); + *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ) = 0; + /* + *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", + 1970, 1, + 1, 0, + 0, 0 + ) = 0; + */ /* Stream publish time */ *ngx_sprintf(publish_time, "%s", available_time) = 0; @@ -889,12 +905,21 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) * Cos segments time counting from it */ ngx_libc_gmtime(start_time, &tm); + *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec ) = 0; + /* + *ngx_sprintf(available_time, "%4d-%02d-%02dT%02d:%02d:%02dZ", + 1970, 1, + 1, 0, + 0, 0 + ) = 0; + */ + /* Stream publish time */ *ngx_sprintf(publish_time, "%s", available_time) = 0; @@ -1237,6 +1262,7 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) ngx_fd_t fd; ngx_buf_t b; ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; ngx_rtmp_dash_frag_t *f; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; @@ -1250,6 +1276,7 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) t->id, t->type, t->earliest_pres_time); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); b.start = buffer; b.end = buffer + sizeof(buffer); @@ -1263,13 +1290,18 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) t->earliest_pres_time, t->latest_pres_time, ctx->cuepoint_starttime, ctx->cuepoint_endtime, ctx->cuepoint_duration, ctx->cuepoint_id); - /* should be ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, - thanks to dashjs - */ - ngx_rtmp_mp4_write_emsg(&b, 0, - ctx->cuepoint_starttime, - ctx->cuepoint_duration, - ctx->cuepoint_id); + if (dacf->ad_markers_timehack) { + /* dashjs bug : use delta as an absolute timestamp */ + ngx_rtmp_mp4_write_emsg(&b, 0, + ctx->cuepoint_starttime, + ctx->cuepoint_duration, + ctx->cuepoint_id); + } else { + ngx_rtmp_mp4_write_emsg(&b, t->earliest_pres_time, + ctx->cuepoint_starttime, + ctx->cuepoint_duration, + ctx->cuepoint_id); + } pos = b.last; b.last = pos; @@ -1322,6 +1354,9 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uD.m4%c", f->timestamp, t->type) = 0; + //*ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4%c", + // f->u_timestamp, t->type) = 0; + fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -1911,7 +1946,9 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, ngx_rtmp_dash_open_fragments(s); f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + f->timestamp = timestamp; + f->u_timestamp = ngx_time() * 1000; } } @@ -2711,6 +2748,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) conf->mspr = NGX_CONF_UNSET; conf->clock_compensation = NGX_CONF_UNSET; conf->ad_markers = NGX_CONF_UNSET; + conf->ad_markers_timehack = NGX_CONF_UNSET; return conf; } @@ -2743,6 +2781,7 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_str_value(conf->clock_helper_uri, prev->clock_helper_uri, ""); ngx_conf_merge_uint_value(conf->ad_markers, prev->ad_markers, NGX_RTMP_DASH_AD_MARKERS_OFF); + ngx_conf_merge_value(conf->ad_markers_timehack, prev->ad_markers_timehack, 0); if (conf->fraglen) { conf->winfrags = conf->playlen / conf->fraglen; diff --git a/dash/ngx_rtmp_dash_templates.h b/dash/ngx_rtmp_dash_templates.h index cad90d466..0e05d5902 100644 --- a/dash/ngx_rtmp_dash_templates.h +++ b/dash/ngx_rtmp_dash_templates.h @@ -133,6 +133,8 @@ " \n" +// " \n" + #define NGX_RTMP_DASH_MANIFEST_TIME \ " \n" From b42863f1d755d1c9907f71b1a26d2dcf2e8b8eec Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 10 Oct 2018 12:13:24 +0000 Subject: [PATCH 105/113] More doc --- DAI.md | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 DAI.md diff --git a/DAI.md b/DAI.md new file mode 100644 index 000000000..124f85b81 --- /dev/null +++ b/DAI.md @@ -0,0 +1,23 @@ + +## AD Insertion markers + +nginx-rtmp module can interpret ad insertion markers message sent by encoder in amf rmtp message. +It then extract the information and write it in an emsg box at the begining of the next chunk. +The emsg box contains the relative start time of the ad-break, its duration, and possibly some metadata. +This is the responsability of the player to watch this event, and to treat it. + +Options to enable ad marker processing are : + +- dash_ad_markers off|on_cuepoint|on_cuepoint_scte35; + + - on_cuepoint is the simple variant (without scte message) + - on_cuepoint_scte35 is the scte35 variant with program_id metadata + +- dash_ad_markers_timehack off|on; + + - off implement the standard timing as described in reference documentation + - on implement a hack on the start time. This is need to be more resilient. Warning this need a patched version of your player. (currently this is what is test and in production with a one line patch on dashjs) + +Currently there is only elemental encoder tested and compliant. + + From f8ec83d56397443f19fd59cc8ae480e12fc91cef Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 10 Oct 2018 12:19:14 +0000 Subject: [PATCH 106/113] Litte documentation fix --- DAI.md | 16 ++++++++++------ DRM.md | 6 +++--- README.md | 2 +- 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/DAI.md b/DAI.md index 124f85b81..23eacf39c 100644 --- a/DAI.md +++ b/DAI.md @@ -8,15 +8,19 @@ This is the responsability of the player to watch this event, and to treat it. Options to enable ad marker processing are : -- dash_ad_markers off|on_cuepoint|on_cuepoint_scte35; +``` +dash_ad_markers off|on_cuepoint|on_cuepoint_scte35; +``` - - on_cuepoint is the simple variant (without scte message) - - on_cuepoint_scte35 is the scte35 variant with program_id metadata + - on_cuepoint is the simple variant (without scte message) + - on_cuepoint_scte35 is the scte35 variant with program_id metadata -- dash_ad_markers_timehack off|on; +``` +dash_ad_markers_timehack off|on; +``` - - off implement the standard timing as described in reference documentation - - on implement a hack on the start time. This is need to be more resilient. Warning this need a patched version of your player. (currently this is what is test and in production with a one line patch on dashjs) + - off implement the standard timing as described in reference documentation + - on implement a hack on the start time. This is need to be more resilient. Warning this need a patched version of your player. (currently this is what is test and in production with a one line patch on dashjs) Currently there is only elemental encoder tested and compliant. diff --git a/DRM.md b/DRM.md index 11797edee..be89e70c6 100644 --- a/DRM.md +++ b/DRM.md @@ -71,10 +71,10 @@ Playready: Bitmovin player seem also to work. -Thanks: +### Thanks: -Thanks to all the opensource communautie. -Special thanks to Eran Kornblau from Kaltura, Joey Parrish, and Jacob Timble. +- Thanks to all the opensource communautie. +- Special thanks to Eran Kornblau from Kaltura, Joey Parrish, and Jacob Timble. diff --git a/README.md b/README.md index dfc9540c0..4b1b84ed0 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Notable new features : - add the possibility to have dash variant like (show below configuration, using ffmpeg to trancode in 3 variants). note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest. you can also use any encoder to directly push the variant. - - add the support of using repetition in manifest to shorten them (optoin dash_repetition) + - add the support of using repetition in manifest to shorten them (option dash_repetition) (thanks to Streamroot) - add the support of common-encryption; currently working DRM are ClearKey/Widevine/Playready (see specific doc [here](DRM.md)) - add the support of ad insertion break event, from rtmp AMF message to dash (InbandEvent in manifest and emsg box in mp4 fragment, see doc [here](DAI.md)) From ef40588fd0b1c706d35c223ce021dcf2f1d279c8 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 10 Oct 2018 12:25:56 +0000 Subject: [PATCH 107/113] Link --- DAI.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/DAI.md b/DAI.md index 23eacf39c..db8e18242 100644 --- a/DAI.md +++ b/DAI.md @@ -4,8 +4,13 @@ nginx-rtmp module can interpret ad insertion markers message sent by encoder in amf rmtp message. It then extract the information and write it in an emsg box at the begining of the next chunk. The emsg box contains the relative start time of the ad-break, its duration, and possibly some metadata. +Enabling ad_marker also add the following inband event in the manifest. This is the responsability of the player to watch this event, and to treat it. +``` + +``` + Options to enable ad marker processing are : ``` @@ -24,4 +29,4 @@ dash_ad_markers_timehack off|on; Currently there is only elemental encoder tested and compliant. - +See [here](https://theyosh.nl/speeltuin/dash/dash.js-2.0.0/samples/ad-insertion/) for the original examples and inspiration. From 97aa4ebacceecf64409972d56b552d43e8c4041b Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 10 Oct 2018 12:33:23 +0000 Subject: [PATCH 108/113] clarification --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 4b1b84ed0..effa4434c 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ Forked from https://github.com/sergey-dryabzhinsky/ which was the most up to dat Notable new features : - - add the possibility to have dash variant like (show below configuration, using ffmpeg to trancode in 3 variants). + - add the possibility to make adaptative streaming (show below configuration, using ffmpeg to trancode in 3 variants, and produce one manifest). note the "max" flag which indicate which representation should have max witdh and height and so use it to create the variant manifest. you can also use any encoder to directly push the variant. - add the support of using repetition in manifest to shorten them (option dash_repetition) (thanks to Streamroot) From c0e7c000ed860ef547a59ecf31f775db63f92b56 Mon Sep 17 00:00:00 2001 From: Raphael Mazelier Date: Wed, 10 Oct 2018 13:37:44 +0000 Subject: [PATCH 109/113] Typo --- DRM.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DRM.md b/DRM.md index be89e70c6..e07ab44a0 100644 --- a/DRM.md +++ b/DRM.md @@ -73,7 +73,7 @@ Bitmovin player seem also to work. ### Thanks: -- Thanks to all the opensource communautie. +- Thanks to all the opensource communauty. - Special thanks to Eran Kornblau from Kaltura, Joey Parrish, and Jacob Timble. From df4113fb4d9177c7cfb0639b5e92b1fadfd4c174 Mon Sep 17 00:00:00 2001 From: Eneas U de Queiroz Date: Thu, 6 Dec 2018 15:54:23 -0200 Subject: [PATCH 110/113] ngx_rtp_cenc.c: move ngx_{config,core}.h to top Fixes compilation error: unknown type name 'cpu_set_t' Signed-off-by: Eneas U de Queiroz --- dash/ngx_rtmp_cenc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_cenc.c b/dash/ngx_rtmp_cenc.c index f0b81e757..e4af2b177 100644 --- a/dash/ngx_rtmp_cenc.c +++ b/dash/ngx_rtmp_cenc.c @@ -1,10 +1,10 @@ +#include +#include #include #include #include -#include -#include #include #include "ngx_rtmp_cenc.h" From 5b06d1cad5f6711667038169b7ed759d749334da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bub=C3=ADk?= Date: Wed, 13 May 2020 19:57:47 +0200 Subject: [PATCH 111/113] arut's e0e278bc7fedd6f7465648d1d20df1a8422d60bf [removed endian-dependent code] --- ngx_rtmp.c | 4 ++ ngx_rtmp.h | 12 +++-- ngx_rtmp_amf.c | 6 +-- ngx_rtmp_flv_module.c | 2 +- ngx_rtmp_handler.c | 108 +++++++++++++++++++++--------------------- 5 files changed, 68 insertions(+), 64 deletions(-) diff --git a/ngx_rtmp.c b/ngx_rtmp.c index ad671d475..011f3ad50 100644 --- a/ngx_rtmp.c +++ b/ngx_rtmp.c @@ -828,6 +828,7 @@ ngx_rtmp_fire_event(ngx_rtmp_session_t *s, ngx_uint_t evt, void * ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n) { +#if (NGX_HAVE_LITTLE_ENDIAN) u_char *d, *s; d = dst; @@ -836,6 +837,9 @@ ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n) while(s >= (u_char*)src) { *d++ = *s--; } +#else + dst = ngx_cpymem(dst, src, n); +#endif return dst; } diff --git a/ngx_rtmp.h b/ngx_rtmp.h index f3a3d6f18..ddc30ea1c 100644 --- a/ngx_rtmp.h +++ b/ngx_rtmp.h @@ -427,22 +427,26 @@ void * ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n); static ngx_inline uint16_t ngx_rtmp_r16(uint16_t n) { - return (n << 8) | (n >> 8); + return ntohs(n); } static ngx_inline uint32_t ngx_rtmp_r32(uint32_t n) { - return (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24); + return ntohl(n); } static ngx_inline uint64_t ngx_rtmp_r64(uint64_t n) { - return (uint64_t) ngx_rtmp_r32((uint32_t) n) << 32 | - ngx_rtmp_r32((uint32_t) (n >> 32)); +#if (NGX_HAVE_LITTLE_ENDIAN) + return (uint64_t) ntohl((uint32_t) n) << 32 | + ntohl((uint32_t) (n >> 32)); +#else + return n; +#endif } diff --git a/ngx_rtmp_amf.c b/ngx_rtmp_amf.c index b904651dc..7c44519da 100644 --- a/ngx_rtmp_amf.c +++ b/ngx_rtmp_amf.c @@ -14,15 +14,11 @@ static ngx_inline void* ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len) { - size_t k; - if (dst == NULL || src == NULL) { return NULL; } - for(k = 0; k < len; ++k) { - ((u_char*)dst)[k] = ((u_char*)src)[len - 1 - k]; - } + ngx_rtmp_rmemcpy(dst, src, len); return dst; } diff --git a/ngx_rtmp_flv_module.c b/ngx_rtmp_flv_module.c index 4776e5419..76ce64752 100644 --- a/ngx_rtmp_flv_module.c +++ b/ngx_rtmp_flv_module.c @@ -445,7 +445,7 @@ ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts) ngx_rtmp_rmemcpy(&size, ngx_rtmp_flv_header + 1, 3); ngx_rtmp_rmemcpy(&h.timestamp, ngx_rtmp_flv_header + 4, 3); - ((u_char *) &h.timestamp)[3] = ngx_rtmp_flv_header[7]; + h.timestamp |= ((uint32_t) ngx_rtmp_flv_header[7] << 24); ctx->offset += (sizeof(ngx_rtmp_flv_header) + size + 4); diff --git a/ngx_rtmp_handler.c b/ngx_rtmp_handler.c index c6b43c8bc..5611ec88c 100644 --- a/ngx_rtmp_handler.c +++ b/ngx_rtmp_handler.c @@ -200,7 +200,7 @@ ngx_rtmp_recv(ngx_event_t *rev) ngx_rtmp_stream_t *st, *st0; ngx_chain_t *in, *head; ngx_buf_t *b; - u_char *p, *pp, *old_pos; + u_char *p, *old_pos; size_t size, fsize, old_size; uint8_t fmt, ext; uint32_t csid, timestamp; @@ -308,14 +308,14 @@ ngx_rtmp_recv(ngx_event_t *rev) if (b->last - p < 1) continue; csid = 64; - csid += *(uint8_t*)p++; + csid += *p++; } else if (csid == 1) { if (b->last - p < 2) continue; csid = 64; - csid += *(uint8_t*)p++; - csid += (uint32_t)256 * (*(uint8_t*)p++); + csid += *p++; + csid += ((uint32_t) *p++ << 8); } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, c->log, 0, @@ -355,40 +355,37 @@ ngx_rtmp_recv(ngx_event_t *rev) if (fmt <= 2 ) { if (b->last - p < 3) continue; - /* timestamp: - * big-endian 3b -> little-endian 4b */ - pp = (u_char*)×tamp; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - pp[3] = 0; + + /* timestamp: big-endian 3 bytes */ + + timestamp = ((uint32_t) *p++ << 16); + timestamp |= ((uint32_t) *p++ << 8); + timestamp |= *p++; ext = (timestamp == 0x00ffffff); if (fmt <= 1) { if (b->last - p < 4) continue; - /* size: - * big-endian 3b -> little-endian 4b - * type: - * 1b -> 1b*/ - pp = (u_char*)&h->mlen; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - pp[3] = 0; - h->type = *(uint8_t*)p++; + + /* size: big-endian 3 bytes */ + + h->mlen = ((uint32_t) *p++ << 16); + h->mlen |= ((uint32_t) *p++ << 8); + h->mlen |= *p++; + + h->type = *p++; if (fmt == 0) { if (b->last - p < 4) continue; - /* stream: - * little-endian 4b -> little-endian 4b */ - pp = (u_char*)&h->msid; - pp[0] = *p++; - pp[1] = *p++; - pp[2] = *p++; - pp[3] = *p++; + + /* stream: little-endian 4 bytes */ + + h->msid = *p++; + h->msid |= ((uint32_t) *p++ << 8); + h->msid |= ((uint32_t) *p++ << 16); + h->msid |= ((uint32_t) *p++ << 24); } } } @@ -397,13 +394,13 @@ ngx_rtmp_recv(ngx_event_t *rev) if (ext) { if (b->last - p < 4) continue; - pp = (u_char*)×tamp; - /* extented time stamp: - * big-endian 4b -> little-endian 4b */ - pp[3] = *p++; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; + + /* timestamp: big-endian 4 bytes */ + + timestamp = ((uint32_t) *p++ << 24); + timestamp |= ((uint32_t) *p++ << 16); + timestamp |= ((uint32_t) *p++ << 8); + timestamp |= *p++; ngx_log_debug1(NGX_LOG_DEBUG_RTMP, c->log, 0, "RTMP extended timestamp %uD", (uint32_t)timestamp); } @@ -584,7 +581,7 @@ ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_header_t *lh, ngx_chain_t *out) { ngx_chain_t *l; - u_char *p, *pp; + u_char *p; ngx_int_t hsize, thsize, nbufs; uint32_t mlen, timestamp, ext_timestamp; static uint8_t hdrsize[] = { 12, 8, 4, 1 }; @@ -677,33 +674,36 @@ ngx_rtmp_prepare_message(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, /* message header */ if (fmt <= 2) { - pp = (u_char*)×tamp; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; + + *p++ = (u_char) (timestamp >> 16); + *p++ = (u_char) (timestamp >> 8); + *p++ = (u_char) timestamp; + if (fmt <= 1) { - pp = (u_char*)&mlen; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; + + *p++ = (u_char) (mlen >> 16); + *p++ = (u_char) (mlen >> 8); + *p++ = (u_char) mlen; + *p++ = h->type; + if (fmt == 0) { - pp = (u_char*)&h->msid; - *p++ = pp[0]; - *p++ = pp[1]; - *p++ = pp[2]; - *p++ = pp[3]; + + *p++ = (u_char) h->msid; + *p++ = (u_char) (h->msid >> 8); + *p++ = (u_char) (h->msid >> 16); + *p++ = (u_char) (h->msid >> 24); } } } /* extended header */ if (ext_timestamp) { - pp = (u_char*)&ext_timestamp; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; + + *p++ = (u_char) (ext_timestamp >> 24); + *p++ = (u_char) (ext_timestamp >> 16); + *p++ = (u_char) (ext_timestamp >> 8); + *p++ = (u_char) ext_timestamp; /* This CONTRADICTS the standard * but that's the way flash client From 7fb255e6700bc11955d276183be9bf01a5227c05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bub=C3=ADk?= Date: Wed, 13 May 2020 22:41:31 +0200 Subject: [PATCH 112/113] arut's 205664d8c5e040a61b36ae0b74ddf17bc16d2150 [fixed sending protocol messages] --- ngx_rtmp_send.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ngx_rtmp_send.c b/ngx_rtmp_send.c index 69dfed955..764e9ef6f 100644 --- a/ngx_rtmp_send.c +++ b/ngx_rtmp_send.c @@ -33,13 +33,13 @@ *(__b->last++) = (u_char)(utype); #define NGX_RTMP_USER_OUT1(v) \ - *(__b->last++) = ((u_char*)&v)[0]; + *(__b->last++) = (u_char) v; #define NGX_RTMP_USER_OUT4(v) \ - *(__b->last++) = ((u_char*)&v)[3]; \ - *(__b->last++) = ((u_char*)&v)[2]; \ - *(__b->last++) = ((u_char*)&v)[1]; \ - *(__b->last++) = ((u_char*)&v)[0]; + *(__b->last++) = (u_char) (v >> 24); \ + *(__b->last++) = (u_char) (v >> 16); \ + *(__b->last++) = (u_char) (v >> 8); \ + *(__b->last++) = (u_char) v; #define NGX_RTMP_USER_END(s) \ ngx_rtmp_prepare_message(s, &__h, NULL, __l); \ From 96ffb76f8d758a9a05580c7d538e2555aeb042af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jan=20Bub=C3=ADk?= Date: Fri, 15 May 2020 02:19:24 +0200 Subject: [PATCH 113/113] More LE/BE compatibility --- hls/ngx_rtmp_hls_module.c | 20 +++- ngx_rtmp.c | 20 ---- ngx_rtmp.h | 39 +++---- ngx_rtmp_amf.c | 55 +++------ ngx_rtmp_bitop.h | 2 +- ngx_rtmp_eval.c | 2 +- ngx_rtmp_flv_module.c | 18 +-- ngx_rtmp_handshake.c | 6 +- ngx_rtmp_mp4_module.c | 240 +++++++++++++++++++------------------- ngx_rtmp_receive.c | 37 ++---- ngx_rtmp_record_module.c | 42 ++----- 11 files changed, 201 insertions(+), 280 deletions(-) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index fbf2bcbf3..ff2b0f7b3 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -296,7 +296,7 @@ static ngx_command_t ngx_rtmp_hls_commands[] = { ngx_conf_set_enum_slot, NGX_RTMP_APP_CONF_OFFSET, offsetof(ngx_rtmp_hls_app_conf_t, allow_client_cache), - &ngx_rtmp_hls_cache }, + &ngx_rtmp_hls_cache }, { ngx_string("hls_variant"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_1MORE, @@ -816,7 +816,7 @@ ngx_rtmp_hls_append_sps_pps(ngx_rtmp_session_t *s, ngx_buf_t *out) return NGX_ERROR; } - ngx_rtmp_rmemcpy(&len, &rlen, 2); + len=ntohs(rlen); ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "hls: header NAL length: %uz", (size_t) len); @@ -2072,7 +2072,21 @@ ngx_rtmp_hls_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } len = 0; - ngx_rtmp_rmemcpy(&len, &rlen, nal_bytes); + + switch (nal_bytes) { + case 1: + len=*(uint8_t*)&rlen; + break; + case 2: + len=ntohs(*(uint16_t*)&rlen); + break; + case 3: + len=n3toh4((u_char*)&rlen); + break; + case 4: + len=ntohl(rlen); + break; + }; if (len == 0) { continue; diff --git a/ngx_rtmp.c b/ngx_rtmp.c index 011f3ad50..e138f1209 100644 --- a/ngx_rtmp.c +++ b/ngx_rtmp.c @@ -825,26 +825,6 @@ ngx_rtmp_fire_event(ngx_rtmp_session_t *s, ngx_uint_t evt, } -void * -ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n) -{ -#if (NGX_HAVE_LITTLE_ENDIAN) - u_char *d, *s; - - d = dst; - s = (u_char*)src + n - 1; - - while(s >= (u_char*)src) { - *d++ = *s--; - } -#else - dst = ngx_cpymem(dst, src, n); -#endif - - return dst; -} - - static ngx_int_t ngx_rtmp_init_process(ngx_cycle_t *cycle) { diff --git a/ngx_rtmp.h b/ngx_rtmp.h index ddc30ea1c..e88de4380 100644 --- a/ngx_rtmp.h +++ b/ngx_rtmp.h @@ -417,29 +417,9 @@ ngx_int_t ngx_rtmp_fire_event(ngx_rtmp_session_t *s, ngx_uint_t evt, ngx_int_t ngx_rtmp_set_chunk_size(ngx_rtmp_session_t *s, ngx_uint_t size); -/* Bit reverse: we need big-endians in many places */ -void * ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n); - -#define ngx_rtmp_rcpymem(dst, src, n) \ - (((u_char*)ngx_rtmp_rmemcpy(dst, src, n)) + (n)) - - -static ngx_inline uint16_t -ngx_rtmp_r16(uint16_t n) -{ - return ntohs(n); -} - - -static ngx_inline uint32_t -ngx_rtmp_r32(uint32_t n) -{ - return ntohl(n); -} - - +/* Bit agnosticism: we need network to host byte-order conversion in many places */ static ngx_inline uint64_t -ngx_rtmp_r64(uint64_t n) +ntohll(uint64_t n) { #if (NGX_HAVE_LITTLE_ENDIAN) return (uint64_t) ntohl((uint32_t) n) << 32 | @@ -449,6 +429,21 @@ ngx_rtmp_r64(uint64_t n) #endif } +static ngx_inline uint32_t +n3toh4(u_char* src) +{ + return ((uint32_t)src[0]<<16)|((uint32_t)src[1]<<8)|src[2]; +} + +static ngx_inline u_char* +h4ton3(u_char* dst, uint32_t src) +{ + dst[0]=(u_char)(src>>16); + dst[1]=(u_char)(src>>8); + dst[2]=(u_char)src; + + return dst+3; +} /* Receiving messages */ ngx_int_t ngx_rtmp_receive_message(ngx_rtmp_session_t *s, diff --git a/ngx_rtmp_amf.c b/ngx_rtmp_amf.c index 7c44519da..465dc01cb 100644 --- a/ngx_rtmp_amf.c +++ b/ngx_rtmp_amf.c @@ -10,19 +10,6 @@ #include "ngx_rtmp.h" #include - -static ngx_inline void* -ngx_rtmp_amf_reverse_copy(void *dst, void* src, size_t len) -{ - if (dst == NULL || src == NULL) { - return NULL; - } - - ngx_rtmp_rmemcpy(dst, src, len); - - return dst; -} - #define NGX_RTMP_AMF_DEBUG_SIZE 72 #ifdef NGX_DEBUG @@ -203,7 +190,7 @@ ngx_rtmp_amf_read_object(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts, return NGX_ERROR; } - ngx_rtmp_amf_reverse_copy(&len, buf, 2); + len=ntohs(*(uint16_t*)&buf[0]); if (!len) break; @@ -254,7 +241,7 @@ ngx_rtmp_amf_read_array(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts, if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK) return NGX_ERROR; - ngx_rtmp_amf_reverse_copy(&len, buf, 4); + len=ntohl(*(uint32_t*)&buf[0]); for (n = 0; n < len; ++n) { if (ngx_rtmp_amf_read(ctx, n < nelts ? &elts[n] : NULL, 1) != NGX_OK) @@ -348,10 +335,9 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts, switch (type) { case NGX_RTMP_AMF_NUMBER: - if (ngx_rtmp_amf_get(ctx, buf, 8) != NGX_OK) { + if (ngx_rtmp_amf_get(ctx, data, 8) != NGX_OK) { return NGX_ERROR; } - ngx_rtmp_amf_reverse_copy(data, buf, 8); break; case NGX_RTMP_AMF_BOOLEAN: @@ -364,7 +350,7 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts, if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) { return NGX_ERROR; } - ngx_rtmp_amf_reverse_copy(&len, buf, 2); + len=ntohs(*(uint16_t*)buf); if (data == NULL) { rc = ngx_rtmp_amf_get(ctx, data, len); @@ -434,14 +420,14 @@ ngx_rtmp_amf_read(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_amf_elt_t *elts, if (ngx_rtmp_amf_get(ctx, buf, 2) != NGX_OK) { return NGX_ERROR; } - ngx_rtmp_amf_reverse_copy(data, buf, 2); + *(uint16_t*)data=ntohs(*(uint16_t*)buf); break; case NGX_RTMP_AMF_INT32: if (ngx_rtmp_amf_get(ctx, buf, 4) != NGX_OK) { return NGX_ERROR; } - ngx_rtmp_amf_reverse_copy(data, buf, 4); + *(uint32_t*)data=ntohs(*(uint32_t*)buf); break; case NGX_RTMP_AMF_END: @@ -472,9 +458,8 @@ ngx_rtmp_amf_write_object(ngx_rtmp_amf_ctx_t *ctx, len = (uint16_t) elts[n].name.len; - if (ngx_rtmp_amf_put(ctx, - ngx_rtmp_amf_reverse_copy(buf, - &len, 2), 2) != NGX_OK) + *(uint16_t*)buf = htons(len); + if (ngx_rtmp_amf_put(ctx, buf, 2) != NGX_OK) { return NGX_ERROR; } @@ -505,9 +490,8 @@ ngx_rtmp_amf_write_array(ngx_rtmp_amf_ctx_t *ctx, u_char buf[4]; len = nelts; - if (ngx_rtmp_amf_put(ctx, - ngx_rtmp_amf_reverse_copy(buf, - &len, 4), 4) != NGX_OK) + *(uint32_t*)buf = htonl(len); + if (ngx_rtmp_amf_put(ctx, buf, 4) != NGX_OK) { return NGX_ERROR; } @@ -550,9 +534,7 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx, switch(type) { case NGX_RTMP_AMF_NUMBER: - if (ngx_rtmp_amf_put(ctx, - ngx_rtmp_amf_reverse_copy(buf, - data, 8), 8) != NGX_OK) + if (ngx_rtmp_amf_put(ctx, data, 8) != NGX_OK) { return NGX_ERROR; } @@ -569,9 +551,8 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx, len = (uint16_t) ngx_strlen((u_char*) data); } - if (ngx_rtmp_amf_put(ctx, - ngx_rtmp_amf_reverse_copy(buf, - &len, 2), 2) != NGX_OK) + *(uint16_t*)buf = htons(len); + if (ngx_rtmp_amf_put(ctx, buf, 2) != NGX_OK) { return NGX_ERROR; } @@ -617,18 +598,16 @@ ngx_rtmp_amf_write(ngx_rtmp_amf_ctx_t *ctx, break; case NGX_RTMP_AMF_INT16: - if (ngx_rtmp_amf_put(ctx, - ngx_rtmp_amf_reverse_copy(buf, - data, 2), 2) != NGX_OK) + *(uint16_t*)buf = htons(*(uint16_t*)data); + if (ngx_rtmp_amf_put(ctx, buf, 2) != NGX_OK) { return NGX_ERROR; } break; case NGX_RTMP_AMF_INT32: - if (ngx_rtmp_amf_put(ctx, - ngx_rtmp_amf_reverse_copy(buf, - data, 4), 4) != NGX_OK) + *(uint32_t*)buf = htonl(*(uint32_t*)data); + if (ngx_rtmp_amf_put(ctx, buf, 4) != NGX_OK) { return NGX_ERROR; } diff --git a/ngx_rtmp_bitop.h b/ngx_rtmp_bitop.h index c954a35f3..25133d284 100644 --- a/ngx_rtmp_bitop.h +++ b/ngx_rtmp_bitop.h @@ -40,7 +40,7 @@ uint64_t ngx_rtmp_bit_read_golomb(ngx_rtmp_bit_reader_t *br); ((uint32_t) ngx_rtmp_bit_read(br, 32)) #define ngx_rtmp_bit_read_64(br) \ - ((uint64_t) ngx_rtmp_read(br, 64)) + ((uint64_t) ngx_rtmp_bit_read(br, 64)) #endif /* _NGX_RTMP_BITOP_H_INCLUDED_ */ diff --git a/ngx_rtmp_eval.c b/ngx_rtmp_eval.c index 1e5195a90..7ee7c7bac 100644 --- a/ngx_rtmp_eval.c +++ b/ngx_rtmp_eval.c @@ -166,7 +166,7 @@ ngx_rtmp_eval(void *ctx, ngx_str_t *in, ngx_rtmp_eval_t **e, ngx_str_t *out, state = ESCAPE; continue; } - + /* fall through */ case ESCAPE: ngx_rtmp_eval_append(&b, &c, 1, log); state = NORMAL; diff --git a/ngx_rtmp_flv_module.c b/ngx_rtmp_flv_module.c index 76ce64752..cd5162f7d 100644 --- a/ngx_rtmp_flv_module.c +++ b/ngx_rtmp_flv_module.c @@ -102,7 +102,7 @@ ngx_rtmp_flv_fill_index(ngx_rtmp_amf_ctx_t *ctx, ngx_rtmp_flv_index_t *idx) return NGX_ERROR; } - ngx_rtmp_rmemcpy(&nelts, b->pos + ctx->offset, 4); + nelts=htonl(*(uint32_t*)(b->pos + ctx->offset)); idx->nelts = nelts; idx->offset = ctx->offset + 4; @@ -201,11 +201,7 @@ ngx_rtmp_flv_init_index(ngx_rtmp_session_t *s, ngx_chain_t *in) static double ngx_rtmp_flv_index_value(void *src) { - double v; - - ngx_rtmp_rmemcpy(&v, src, 8); - - return v; + return *(double*)src; } @@ -352,8 +348,7 @@ ngx_rtmp_flv_read_meta(ngx_rtmp_session_t *s, ngx_file_t *f) h.msid = NGX_RTMP_MSID; h.csid = NGX_RTMP_CSID_AMF; - size = 0; - ngx_rtmp_rmemcpy(&size, ngx_rtmp_flv_header + 1, 3); + size = n3toh4(ngx_rtmp_flv_header + 1); ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "flv: metadata size=%D", size); @@ -440,11 +435,8 @@ ngx_rtmp_flv_send(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_uint_t *ts) h.msid = NGX_RTMP_MSID; h.type = ngx_rtmp_flv_header[0]; - size = 0; - - ngx_rtmp_rmemcpy(&size, ngx_rtmp_flv_header + 1, 3); - ngx_rtmp_rmemcpy(&h.timestamp, ngx_rtmp_flv_header + 4, 3); - + size = n3toh4(ngx_rtmp_flv_header + 1); + h.timestamp = n3toh4(ngx_rtmp_flv_header + 4); h.timestamp |= ((uint32_t) ngx_rtmp_flv_header[7] << 24); ctx->offset += (sizeof(ngx_rtmp_flv_header) + size + 4); diff --git a/ngx_rtmp_handshake.c b/ngx_rtmp_handshake.c index 409d9a0dd..8590cdcb2 100644 --- a/ngx_rtmp_handshake.c +++ b/ngx_rtmp_handshake.c @@ -264,7 +264,8 @@ ngx_rtmp_handshake_create_challenge(ngx_rtmp_session_t *s, b = s->hs_buf; b->last = b->pos = b->start; *b->last++ = '\x03'; - b->last = ngx_rtmp_rcpymem(b->last, &s->epoch, 4); + *(uint32_t*)b->last=htonl(s->epoch); + b->last +=4; b->last = ngx_cpymem(b->last, version, 4); ngx_rtmp_fill_random_buffer(b); ++b->pos; @@ -292,8 +293,7 @@ ngx_rtmp_handshake_parse_challenge(ngx_rtmp_session_t *s, return NGX_ERROR; } ++b->pos; - s->peer_epoch = 0; - ngx_rtmp_rmemcpy(&s->peer_epoch, b->pos, 4); + s->peer_epoch = ntohl(*(uint32_t*)b->pos); p = b->pos + 4; ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, diff --git a/ngx_rtmp_mp4_module.c b/ngx_rtmp_mp4_module.c index 0259ca2f6..e39bb1bdb 100644 --- a/ngx_rtmp_mp4_module.c +++ b/ngx_rtmp_mp4_module.c @@ -528,9 +528,9 @@ ngx_rtmp_mp4_parse_mdhd(ngx_rtmp_session_t *s, u_char *pos, u_char *last) } pos += 12; - t->time_scale = ngx_rtmp_r32(*(uint32_t *) pos); + t->time_scale = ntohl(*(uint32_t *) pos); pos += 4; - t->duration = ngx_rtmp_r32(*(uint32_t *) pos); + t->duration = ntohl(*(uint32_t *) pos); break; case 1: @@ -539,9 +539,9 @@ ngx_rtmp_mp4_parse_mdhd(ngx_rtmp_session_t *s, u_char *pos, u_char *last) } pos += 20; - t->time_scale = ngx_rtmp_r32(*(uint32_t *) pos); + t->time_scale = ntohl(*(uint32_t *) pos); pos += 4; - t->duration = ngx_rtmp_r64(*(uint64_t *) pos); + t->duration = ntohll(*(uint64_t *) pos); break; default: @@ -616,11 +616,11 @@ ngx_rtmp_mp4_parse_video(ngx_rtmp_session_t *s, u_char *pos, u_char *last, pos += 24; - ctx->width = ngx_rtmp_r16(*(uint16_t *) pos); + ctx->width = ntohs(*(uint16_t *) pos); pos += 2; - ctx->height = ngx_rtmp_r16(*(uint16_t *) pos); + ctx->height = ntohs(*(uint16_t *) pos); pos += 52; @@ -660,19 +660,19 @@ ngx_rtmp_mp4_parse_audio(ngx_rtmp_session_t *s, u_char *pos, u_char *last, pos += 8; - version = ngx_rtmp_r16(*(uint16_t *) pos); + version = ntohs(*(uint16_t *) pos); pos += 8; - ctx->nchannels = ngx_rtmp_r16(*(uint16_t *) pos); + ctx->nchannels = ntohs(*(uint16_t *) pos); pos += 2; - ctx->sample_size = ngx_rtmp_r16(*(uint16_t *) pos); + ctx->sample_size = ntohs(*(uint16_t *) pos); pos += 6; - ctx->sample_rate = ngx_rtmp_r16(*(uint16_t *) pos); + ctx->sample_rate = ntohs(*(uint16_t *) pos); pos += 4; @@ -862,7 +862,7 @@ ngx_rtmp_mp4_parse_es(ngx_rtmp_session_t *s, u_char *pos, u_char *last) return NGX_ERROR; } - id = ngx_rtmp_r16(*(uint16_t *) pos); + id = ntohs(*(uint16_t *) pos); pos += 2; flags = *(uint8_t *) pos; @@ -1018,13 +1018,13 @@ ngx_rtmp_mp4_parse_stsc(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->chunks = (ngx_rtmp_mp4_chunks_t *) pos; - if (pos + sizeof(*t->chunks) + ngx_rtmp_r32(t->chunks->entry_count) * + if (pos + sizeof(*t->chunks) + ntohl(t->chunks->entry_count) * sizeof(t->chunks->entries[0]) <= last) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: chunks entries=%uD", - ngx_rtmp_r32(t->chunks->entry_count)); + ntohl(t->chunks->entry_count)); return NGX_OK; } @@ -1049,13 +1049,13 @@ ngx_rtmp_mp4_parse_stts(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->times = (ngx_rtmp_mp4_times_t *) pos; - if (pos + sizeof(*t->times) + ngx_rtmp_r32(t->times->entry_count) * + if (pos + sizeof(*t->times) + ntohl(t->times->entry_count) * sizeof(t->times->entries[0]) <= last) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: times entries=%uD", - ngx_rtmp_r32(t->times->entry_count)); + ntohl(t->times->entry_count)); return NGX_OK; } @@ -1080,13 +1080,13 @@ ngx_rtmp_mp4_parse_ctts(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->delays = (ngx_rtmp_mp4_delays_t *) pos; - if (pos + sizeof(*t->delays) + ngx_rtmp_r32(t->delays->entry_count) * + if (pos + sizeof(*t->delays) + ntohl(t->delays->entry_count) * sizeof(t->delays->entries[0]) <= last) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: delays entries=%uD", - ngx_rtmp_r32(t->delays->entry_count)); + ntohl(t->delays->entry_count)); return NGX_OK; } @@ -1111,13 +1111,13 @@ ngx_rtmp_mp4_parse_stss(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->keys = (ngx_rtmp_mp4_keys_t *) pos; - if (pos + sizeof(*t->keys) + ngx_rtmp_r32(t->keys->entry_count) * + if (pos + sizeof(*t->keys) + ntohl(t->keys->entry_count) * sizeof(t->keys->entries[0]) <= last) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: keys entries=%uD", - ngx_rtmp_r32(t->keys->entry_count)); + ntohl(t->keys->entry_count)); return NGX_OK; } @@ -1145,18 +1145,18 @@ ngx_rtmp_mp4_parse_stsz(ngx_rtmp_session_t *s, u_char *pos, u_char *last) if (pos + sizeof(*t->sizes) <= last && t->sizes->sample_size) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: sizes size=%uD", - ngx_rtmp_r32(t->sizes->sample_size)); + ntohl(t->sizes->sample_size)); return NGX_OK; } - if (pos + sizeof(*t->sizes) + ngx_rtmp_r32(t->sizes->sample_count) * + if (pos + sizeof(*t->sizes) + ntohl(t->sizes->sample_count) * sizeof(t->sizes->entries[0]) <= last) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: sizes entries=%uD", - ngx_rtmp_r32(t->sizes->sample_count)); + ntohl(t->sizes->sample_count)); return NGX_OK; } @@ -1181,14 +1181,14 @@ ngx_rtmp_mp4_parse_stz2(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->sizes2 = (ngx_rtmp_mp4_sizes2_t *) pos; - if (pos + sizeof(*t->sizes) + ngx_rtmp_r32(t->sizes2->sample_count) * - ngx_rtmp_r32(t->sizes2->field_size) / 8 + if (pos + sizeof(*t->sizes) + ntohl(t->sizes2->sample_count) * + ntohl(t->sizes2->field_size) / 8 <= last) { ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: sizes2 field_size=%uD entries=%uD", - ngx_rtmp_r32(t->sizes2->field_size), - ngx_rtmp_r32(t->sizes2->sample_count)); + ntohl(t->sizes2->field_size), + ntohl(t->sizes2->sample_count)); return NGX_OK; } @@ -1213,13 +1213,13 @@ ngx_rtmp_mp4_parse_stco(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->offsets = (ngx_rtmp_mp4_offsets_t *) pos; - if (pos + sizeof(*t->offsets) + ngx_rtmp_r32(t->offsets->entry_count) * + if (pos + sizeof(*t->offsets) + ntohl(t->offsets->entry_count) * sizeof(t->offsets->entries[0]) <= last) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: offsets entries=%uD", - ngx_rtmp_r32(t->offsets->entry_count)); + ntohl(t->offsets->entry_count)); return NGX_OK; } @@ -1244,13 +1244,13 @@ ngx_rtmp_mp4_parse_co64(ngx_rtmp_session_t *s, u_char *pos, u_char *last) t->offsets64 = (ngx_rtmp_mp4_offsets64_t *) pos; - if (pos + sizeof(*t->offsets64) + ngx_rtmp_r32(t->offsets64->entry_count) * + if (pos + sizeof(*t->offsets64) + ntohl(t->offsets64->entry_count) * sizeof(t->offsets64->entries[0]) <= last) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: offsets64 entries=%uD", - ngx_rtmp_r32(t->offsets64->entry_count)); + ntohl(t->offsets64->entry_count)); return NGX_OK; } @@ -1275,7 +1275,7 @@ ngx_rtmp_mp4_parse(ngx_rtmp_session_t *s, u_char *pos, u_char *last) } hdr = (uint32_t *) pos; - size = ngx_rtmp_r32(hdr[0]); + size = ntohl(hdr[0]); tag = hdr[1]; if (pos + size > last) { @@ -1318,11 +1318,11 @@ ngx_rtmp_mp4_next_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) cr = &t->cursor; - if (cr->time_pos >= ngx_rtmp_r32(t->times->entry_count)) { + if (cr->time_pos >= ntohl(t->times->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui time[%ui/%uD] overflow", t->id, cr->time_pos, - ngx_rtmp_r32(t->times->entry_count)); + ntohl(t->times->entry_count)); return NGX_ERROR; } @@ -1330,22 +1330,22 @@ ngx_rtmp_mp4_next_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) te = &t->times->entries[cr->time_pos]; cr->last_timestamp = cr->timestamp; - cr->timestamp += ngx_rtmp_r32(te->sample_delta); + cr->timestamp += ntohl(te->sample_delta); cr->not_first = 1; ngx_log_debug8(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui time[%ui] [%ui/%uD][%ui/%uD]=%uD t=%uD", t->id, cr->pos, cr->time_pos, - ngx_rtmp_r32(t->times->entry_count), - cr->time_count, ngx_rtmp_r32(te->sample_count), - ngx_rtmp_r32(te->sample_delta), + ntohl(t->times->entry_count), + cr->time_count, ntohl(te->sample_count), + ntohl(te->sample_delta), cr->timestamp); cr->time_count++; cr->pos++; - if (cr->time_count >= ngx_rtmp_r32(te->sample_count)) { + if (cr->time_count >= ntohl(te->sample_count)) { cr->time_pos++; cr->time_count = 0; } @@ -1370,8 +1370,8 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t, te = t->times->entries; - while (cr->time_pos < ngx_rtmp_r32(t->times->entry_count)) { - dt = ngx_rtmp_r32(te->sample_delta) * ngx_rtmp_r32(te->sample_count); + while (cr->time_pos < ntohl(t->times->entry_count)) { + dt = ntohl(te->sample_delta) * ntohl(te->sample_count); if (cr->timestamp + dt >= timestamp) { if (te->sample_delta == 0) { @@ -1379,24 +1379,24 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t, } cr->time_count = (timestamp - cr->timestamp) / - ngx_rtmp_r32(te->sample_delta); - cr->timestamp += ngx_rtmp_r32(te->sample_delta) * cr->time_count; + ntohl(te->sample_delta); + cr->timestamp += ntohl(te->sample_delta) * cr->time_count; cr->pos += cr->time_count; break; } cr->timestamp += dt; - cr->pos += ngx_rtmp_r32(te->sample_count); + cr->pos += ntohl(te->sample_count); cr->time_pos++; te++; } - if (cr->time_pos >= ngx_rtmp_r32(t->times->entry_count)) { + if (cr->time_pos >= ntohl(t->times->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek time[%ui/%uD] overflow", t->id, cr->time_pos, - ngx_rtmp_r32(t->times->entry_count)); + ntohl(t->times->entry_count)); return NGX_ERROR; } @@ -1405,10 +1405,10 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t, "mp4: track#%ui seek time[%ui] [%ui/%uD][%ui/%uD]=%uD " "t=%uD", t->id, cr->pos, cr->time_pos, - ngx_rtmp_r32(t->times->entry_count), + ntohl(t->times->entry_count), cr->time_count, - ngx_rtmp_r32(te->sample_count), - ngx_rtmp_r32(te->sample_delta), + ntohl(te->sample_count), + ntohl(te->sample_delta), cr->timestamp); return NGX_OK; @@ -1433,44 +1433,44 @@ ngx_rtmp_mp4_update_offset(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) chunk = cr->chunk - 1; if (t->offsets) { - if (chunk >= ngx_rtmp_r32(t->offsets->entry_count)) { + if (chunk >= ntohl(t->offsets->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui offset[%ui/%uD] overflow", t->id, cr->chunk, - ngx_rtmp_r32(t->offsets->entry_count)); + ntohl(t->offsets->entry_count)); return NGX_ERROR; } - cr->offset = (off_t) ngx_rtmp_r32(t->offsets->entries[chunk]); + cr->offset = (off_t) ntohl(t->offsets->entries[chunk]); cr->size = 0; ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui offset[%ui/%uD]=%O", t->id, cr->chunk, - ngx_rtmp_r32(t->offsets->entry_count), + ntohl(t->offsets->entry_count), cr->offset); return NGX_OK; } if (t->offsets64) { - if (chunk >= ngx_rtmp_r32(t->offsets64->entry_count)) { + if (chunk >= ntohl(t->offsets64->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui offset64[%ui/%uD] overflow", t->id, cr->chunk, - ngx_rtmp_r32(t->offsets->entry_count)); + ntohl(t->offsets->entry_count)); return NGX_ERROR; } - cr->offset = (off_t) ngx_rtmp_r64(t->offsets64->entries[chunk]); + cr->offset = (off_t) ntohll(t->offsets64->entries[chunk]); cr->size = 0; ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui offset64[%ui/%uD]=%O", t->id, cr->chunk, - ngx_rtmp_r32(t->offsets->entry_count), + ntohl(t->offsets->entry_count), cr->offset); return NGX_OK; @@ -1493,11 +1493,11 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) cr = &t->cursor; - if (cr->chunk_pos >= ngx_rtmp_r32(t->chunks->entry_count)) { + if (cr->chunk_pos >= ntohl(t->chunks->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui chunk[%ui/%uD] overflow", t->id, cr->chunk_pos, - ngx_rtmp_r32(t->chunks->entry_count)); + ntohl(t->chunks->entry_count)); return NGX_ERROR; } @@ -1506,13 +1506,13 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) cr->chunk_count++; - if (cr->chunk_count >= ngx_rtmp_r32(ce->samples_per_chunk)) { + if (cr->chunk_count >= ntohl(ce->samples_per_chunk)) { cr->chunk_count = 0; cr->chunk++; - if (cr->chunk_pos + 1 < ngx_rtmp_r32(t->chunks->entry_count)) { + if (cr->chunk_pos + 1 < ntohl(t->chunks->entry_count)) { nce = ce + 1; - if (cr->chunk >= ngx_rtmp_r32(nce->first_chunk)) { + if (cr->chunk >= ntohl(nce->first_chunk)) { cr->chunk_pos++; ce = nce; } @@ -1527,10 +1527,10 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui chunk[%ui/%uD][%uD..%ui][%ui/%uD]", t->id, cr->chunk_pos, - ngx_rtmp_r32(t->chunks->entry_count), - ngx_rtmp_r32(ce->first_chunk), + ntohl(t->chunks->entry_count), + ntohl(ce->first_chunk), cr->chunk, cr->chunk_count, - ngx_rtmp_r32(ce->samples_per_chunk)); + ntohl(ce->samples_per_chunk)); if (new_chunk) { @@ -1558,12 +1558,12 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) ce = t->chunks->entries; pos = 0; - while (cr->chunk_pos + 1 < ngx_rtmp_r32(t->chunks->entry_count)) { + while (cr->chunk_pos + 1 < ntohl(t->chunks->entry_count)) { nce = ce + 1; - dpos = (ngx_rtmp_r32(nce->first_chunk) - - ngx_rtmp_r32(ce->first_chunk)) * - ngx_rtmp_r32(ce->samples_per_chunk); + dpos = (ntohl(nce->first_chunk) - + ntohl(ce->first_chunk)) * + ntohl(ce->samples_per_chunk); if (pos + dpos > cr->pos) { break; @@ -1578,20 +1578,20 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) return NGX_ERROR; } - dchunk = (cr->pos - pos) / ngx_rtmp_r32(ce->samples_per_chunk); + dchunk = (cr->pos - pos) / ntohl(ce->samples_per_chunk); - cr->chunk = ngx_rtmp_r32(ce->first_chunk) + dchunk; + cr->chunk = ntohl(ce->first_chunk) + dchunk; cr->chunk_pos = (ngx_uint_t) (ce - t->chunks->entries); cr->chunk_count = (ngx_uint_t) (cr->pos - pos - dchunk * - ngx_rtmp_r32(ce->samples_per_chunk)); + ntohl(ce->samples_per_chunk)); ngx_log_debug7(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek chunk[%ui/%uD][%uD..%ui][%ui/%uD]", t->id, cr->chunk_pos, - ngx_rtmp_r32(t->chunks->entry_count), - ngx_rtmp_r32(ce->first_chunk), + ntohl(t->chunks->entry_count), + ntohl(ce->first_chunk), cr->chunk, cr->chunk_count, - ngx_rtmp_r32(ce->samples_per_chunk)); + ntohl(ce->samples_per_chunk)); return ngx_rtmp_mp4_update_offset(s, t); } @@ -1608,7 +1608,7 @@ ngx_rtmp_mp4_next_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) if (t->sizes) { if (t->sizes->sample_size) { - cr->size = ngx_rtmp_r32(t->sizes->sample_size); + cr->size = ntohl(t->sizes->sample_size); ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui size fix=%uz", @@ -1619,32 +1619,32 @@ ngx_rtmp_mp4_next_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) cr->size_pos++; - if (cr->size_pos >= ngx_rtmp_r32(t->sizes->sample_count)) { + if (cr->size_pos >= ntohl(t->sizes->sample_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui size[%ui/%uD] overflow", t->id, cr->size_pos, - ngx_rtmp_r32(t->sizes->sample_count)); + ntohl(t->sizes->sample_count)); return NGX_ERROR; } - cr->size = ngx_rtmp_r32(t->sizes->entries[cr->size_pos]); + cr->size = ntohl(t->sizes->entries[cr->size_pos]); ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui size[%ui/%uD]=%uz", t->id, cr->size_pos, - ngx_rtmp_r32(t->sizes->sample_count), + ntohl(t->sizes->sample_count), cr->size); return NGX_OK; } if (t->sizes2) { - if (cr->size_pos >= ngx_rtmp_r32(t->sizes2->sample_count)) { + if (cr->size_pos >= ntohl(t->sizes2->sample_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui size[%ui/%uD] overflow", t->id, cr->size_pos, - ngx_rtmp_r32(t->sizes2->sample_count)); + ntohl(t->sizes2->sample_count)); return NGX_ERROR; } @@ -1672,7 +1672,7 @@ ngx_rtmp_mp4_seek_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) if (t->sizes) { if (t->sizes->sample_size) { - cr->size = ngx_rtmp_r32(t->sizes->sample_size); + cr->size = ntohl(t->sizes->sample_size); cr->offset += cr->size * cr->chunk_count; @@ -1683,37 +1683,37 @@ ngx_rtmp_mp4_seek_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) return NGX_OK; } - if (cr->pos >= ngx_rtmp_r32(t->sizes->sample_count)) { + if (cr->pos >= ntohl(t->sizes->sample_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek size[%ui/%uD] overflow", t->id, cr->pos, - ngx_rtmp_r32(t->sizes->sample_count)); + ntohl(t->sizes->sample_count)); return NGX_ERROR; } for (pos = 1; pos <= cr->chunk_count; ++pos) { - cr->offset += ngx_rtmp_r32(t->sizes->entries[cr->pos - pos]); + cr->offset += ntohl(t->sizes->entries[cr->pos - pos]); } cr->size_pos = cr->pos; - cr->size = ngx_rtmp_r32(t->sizes->entries[cr->size_pos]); + cr->size = ntohl(t->sizes->entries[cr->size_pos]); ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek size[%ui/%uD]=%uz", t->id, cr->size_pos, - ngx_rtmp_r32(t->sizes->sample_count), + ntohl(t->sizes->sample_count), cr->size); return NGX_OK; } if (t->sizes2) { - if (cr->size_pos >= ngx_rtmp_r32(t->sizes2->sample_count)) { + if (cr->size_pos >= ntohl(t->sizes2->sample_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek size2[%ui/%uD] overflow", t->id, cr->size_pos, - ngx_rtmp_r32(t->sizes->sample_count)); + ntohl(t->sizes->sample_count)); return NGX_ERROR; } @@ -1744,11 +1744,11 @@ ngx_rtmp_mp4_next_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) cr->key_pos++; } - if (cr->key_pos >= ngx_rtmp_r32(t->keys->entry_count)) { + if (cr->key_pos >= ntohl(t->keys->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui key[%ui/%uD] overflow", t->id, cr->key_pos, - ngx_rtmp_r32(t->keys->entry_count)); + ntohl(t->keys->entry_count)); cr->key = 0; @@ -1756,13 +1756,13 @@ ngx_rtmp_mp4_next_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) } ke = &t->keys->entries[cr->key_pos]; - cr->key = (cr->pos + 1 == ngx_rtmp_r32(*ke)); + cr->key = (cr->pos + 1 == ntohl(*ke)); ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui key[%ui/%uD][%ui/%uD]=%s", t->id, cr->key_pos, - ngx_rtmp_r32(t->keys->entry_count), - cr->pos, ngx_rtmp_r32(*ke), + ntohl(t->keys->entry_count), + cr->pos, ntohl(*ke), cr->key ? "match" : "miss"); return NGX_OK; @@ -1782,27 +1782,27 @@ ngx_rtmp_mp4_seek_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) return NGX_OK; } - while (cr->key_pos < ngx_rtmp_r32(t->keys->entry_count)) { - if (ngx_rtmp_r32(t->keys->entries[cr->key_pos]) > cr->pos) { + while (cr->key_pos < ntohl(t->keys->entry_count)) { + if (ntohl(t->keys->entries[cr->key_pos]) > cr->pos) { break; } cr->key_pos++; } - if (cr->key_pos >= ngx_rtmp_r32(t->keys->entry_count)) { + if (cr->key_pos >= ntohl(t->keys->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek key[%ui/%uD] overflow", t->id, cr->key_pos, - ngx_rtmp_r32(t->keys->entry_count)); + ntohl(t->keys->entry_count)); return NGX_OK; } ke = &t->keys->entries[cr->key_pos]; - /*cr->key = (cr->pos + 1 == ngx_rtmp_r32(*ke));*/ + /*cr->key = (cr->pos + 1 == ntohl(*ke));*/ /* distance to the next keyframe */ - dpos = ngx_rtmp_r32(*ke) - cr->pos - 1; + dpos = ntohl(*ke) - cr->pos - 1; cr->key = 1; /* TODO: range version needed */ @@ -1810,13 +1810,13 @@ ngx_rtmp_mp4_seek_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) ngx_rtmp_mp4_next_time(s, t); } -/* cr->key = (cr->pos + 1 == ngx_rtmp_r32(*ke));*/ +/* cr->key = (cr->pos + 1 == ntohl(*ke));*/ ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek key[%ui/%uD][%ui/%uD]=%s", t->id, cr->key_pos, - ngx_rtmp_r32(t->keys->entry_count), - cr->pos, ngx_rtmp_r32(*ke), + ntohl(t->keys->entry_count), + cr->pos, ntohl(*ke), cr->key ? "match" : "miss"); return NGX_OK; @@ -1835,11 +1835,11 @@ ngx_rtmp_mp4_next_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) return NGX_OK; } - if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) { + if (cr->delay_pos >= ntohl(t->delays->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui delay[%ui/%uD] overflow", t->id, cr->delay_pos, - ngx_rtmp_r32(t->delays->entry_count)); + ntohl(t->delays->entry_count)); return NGX_OK; } @@ -1847,29 +1847,29 @@ ngx_rtmp_mp4_next_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) cr->delay_count++; de = &t->delays->entries[cr->delay_pos]; - if (cr->delay_count >= ngx_rtmp_r32(de->sample_count)) { + if (cr->delay_count >= ntohl(de->sample_count)) { cr->delay_pos++; de++; cr->delay_count = 0; } - if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) { + if (cr->delay_pos >= ntohl(t->delays->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui delay[%ui/%uD] overflow", t->id, cr->delay_pos, - ngx_rtmp_r32(t->delays->entry_count)); + ntohl(t->delays->entry_count)); return NGX_OK; } - cr->delay = ngx_rtmp_r32(de->sample_offset); + cr->delay = ntohl(de->sample_offset); ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui delay[%ui/%uD][%ui/%uD]=%ui", t->id, cr->delay_pos, - ngx_rtmp_r32(t->delays->entry_count), + ntohl(t->delays->entry_count), cr->delay_count, - ngx_rtmp_r32(de->sample_count), cr->delay); + ntohl(de->sample_count), cr->delay); return NGX_OK; } @@ -1891,12 +1891,12 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) pos = 0; de = t->delays->entries; - while (cr->delay_pos < ngx_rtmp_r32(t->delays->entry_count)) { - dpos = ngx_rtmp_r32(de->sample_count); + while (cr->delay_pos < ntohl(t->delays->entry_count)) { + dpos = ntohl(de->sample_count); if (pos + dpos > cr->pos) { cr->delay_count = cr->pos - pos; - cr->delay = ngx_rtmp_r32(de->sample_offset); + cr->delay = ntohl(de->sample_offset); break; } @@ -1905,11 +1905,11 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) de++; } - if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) { + if (cr->delay_pos >= ntohl(t->delays->entry_count)) { ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek delay[%ui/%uD] overflow", t->id, cr->delay_pos, - ngx_rtmp_r32(t->delays->entry_count)); + ntohl(t->delays->entry_count)); return NGX_OK; } @@ -1917,9 +1917,9 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) ngx_log_debug6(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek delay[%ui/%uD][%ui/%uD]=%ui", t->id, cr->delay_pos, - ngx_rtmp_r32(t->delays->entry_count), + ntohl(t->delays->entry_count), cr->delay_count, - ngx_rtmp_r32(de->sample_count), cr->delay); + ntohl(de->sample_count), cr->delay); return NGX_OK; } @@ -2348,7 +2348,7 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex, return NGX_ERROR; } - size = (size_t) ngx_rtmp_r32(hdr[0]); + size = (size_t) ntohl(hdr[0]); shift = sizeof(hdr); if (size == 1) { @@ -2362,7 +2362,7 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s, ngx_file_t *f, ngx_int_t aindex, return NGX_ERROR; } - size = (size_t) ngx_rtmp_r64(extended_size); + size = (size_t) ntohll(extended_size); shift += sizeof(extended_size); } else if (size == 0) { diff --git a/ngx_rtmp_receive.c b/ngx_rtmp_receive.c index 73d617cf2..9c8705691 100644 --- a/ngx_rtmp_receive.c +++ b/ngx_rtmp_receive.c @@ -17,7 +17,6 @@ ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_buf_t *b; - u_char *p; uint32_t val; uint8_t limit; @@ -30,11 +29,7 @@ ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s, return NGX_OK; } - p = (u_char*)&val; - p[0] = b->pos[3]; - p[1] = b->pos[2]; - p[2] = b->pos[1]; - p[3] = b->pos[0]; + val=ntohl(*(uint32_t*)&b->pos[0]); switch(h->type) { case NGX_RTMP_MSG_CHUNK_SIZE: @@ -88,7 +83,6 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { ngx_buf_t *b; - u_char *p; uint16_t evt; uint32_t val; @@ -101,21 +95,13 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } - p = (u_char*)&evt; - - p[0] = b->pos[1]; - p[1] = b->pos[0]; + evt=ntohs(*(uint16_t*)&b->pos[0]); ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "RTMP recv user evt %s (%i)", ngx_rtmp_user_message_type(evt), (ngx_int_t) evt); - p = (u_char *) &val; - - p[0] = b->pos[5]; - p[1] = b->pos[4]; - p[2] = b->pos[3]; - p[3] = b->pos[2]; + val=ntohl(*(uint32_t*)&b->pos[2]); switch(evt) { case NGX_RTMP_USER_STREAM_BEGIN: @@ -164,12 +150,7 @@ ngx_rtmp_user_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } - p = (u_char *) &v.buflen; - - p[0] = b->pos[9]; - p[1] = b->pos[8]; - p[2] = b->pos[7]; - p[3] = b->pos[6]; + v.buflen=ntohl(*(uint32_t*)&b->pos[6]); ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "receive: set_buflen msid=%uD buflen=%uD", @@ -240,18 +221,20 @@ ngx_rtmp_fetch_uint8(ngx_chain_t **in, uint8_t *ret) static ngx_int_t ngx_rtmp_fetch_uint32(ngx_chain_t **in, uint32_t *ret, ngx_int_t n) { - u_char *r = (u_char *) ret; + u_char b; + uint32_t val=0; ngx_int_t rc; - *ret = 0; - while (--n >= 0) { - rc = ngx_rtmp_fetch(in, &r[n]); + rc = ngx_rtmp_fetch(in, &b); if (rc != NGX_OK) { + *ret = 0; return rc; } + val = (val<<8)|b; } + *ret=val; return NGX_OK; } diff --git a/ngx_rtmp_record_module.c b/ngx_rtmp_record_module.c index c7db8edfa..abba56e15 100644 --- a/ngx_rtmp_record_module.c +++ b/ngx_rtmp_record_module.c @@ -454,7 +454,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s, ngx_err_t err; ngx_str_t path; ngx_int_t mode, create_mode; - u_char buf[8], *p; + u_char buf[8]; off_t file_size; uint32_t tag_size, mlen, timestamp; @@ -551,11 +551,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s, goto done; } - p = (u_char *) &tag_size; - p[0] = buf[3]; - p[1] = buf[2]; - p[2] = buf[1]; - p[3] = buf[0]; + tag_size=ntohl(*(uint32_t*)&buf[0]); if (tag_size == 0 || tag_size + 4 > file_size) { file_size = 0; @@ -569,11 +565,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s, goto done; } - p = (u_char *) &mlen; - p[0] = buf[3]; - p[1] = buf[2]; - p[2] = buf[1]; - p[3] = 0; + mlen=n3toh4(&buf[1]); if (tag_size != mlen + 11) { ngx_log_error(NGX_LOG_CRIT, s->connection->log, ngx_errno, @@ -582,11 +574,7 @@ ngx_rtmp_record_node_open(ngx_rtmp_session_t *s, goto done; } - p = (u_char *) ×tamp; - p[3] = buf[7]; - p[0] = buf[6]; - p[1] = buf[5]; - p[2] = buf[4]; + timestamp=n3toh4(&buf[4])|((uint32_t)buf[7]<<24); done: rctx->file.offset = file_size; @@ -891,7 +879,7 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in, ngx_int_t inc_nframes) { - u_char hdr[11], *p, *ph; + u_char hdr[11], *ph; uint32_t timestamp, tag_size; ngx_rtmp_record_app_conf_t *rracf; @@ -937,16 +925,10 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s, *ph++ = (u_char)h->type; - p = (u_char*)&h->mlen; - *ph++ = p[2]; - *ph++ = p[1]; - *ph++ = p[0]; + ph = h4ton3(ph, h->mlen); - p = (u_char*)×tamp; - *ph++ = p[2]; - *ph++ = p[1]; - *ph++ = p[0]; - *ph++ = p[3]; + ph = h4ton3(ph, timestamp); + *ph++ = (u_char)(timestamp>>24); *ph++ = 0; *ph++ = 0; @@ -985,12 +967,8 @@ ngx_rtmp_record_write_frame(ngx_rtmp_session_t *s, /* write tag size */ ph = hdr; - p = (u_char*)&tag_size; - - *ph++ = p[3]; - *ph++ = p[2]; - *ph++ = p[1]; - *ph++ = p[0]; + *(uint32_t*)ph = htonl(tag_size); + ph += 4; if (ngx_write_file(&rctx->file, hdr, ph - hdr, rctx->file.offset)