diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 8fe9970..9718be7 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -36,6 +36,7 @@ static ngx_int_t ngx_rtmp_hls_ensure_directory(ngx_rtmp_session_t *s, typedef struct { uint64_t id; uint64_t key_id; + ngx_str_t *datetime; double duration; unsigned active:1; unsigned discont:1; /* before */ @@ -101,6 +102,7 @@ typedef struct { ngx_flag_t nested; ngx_str_t path; ngx_uint_t naming; + ngx_uint_t datetime; ngx_uint_t slicing; ngx_uint_t type; ngx_path_t *slot; @@ -122,6 +124,11 @@ typedef struct { #define NGX_RTMP_HLS_NAMING_SYSTEM 3 +#define NGX_RTMP_HLS_DATETIME_NONE 1 +#define NGX_RTMP_HLS_DATETIME_SYSTEM 2 +#define NGX_RTMP_HLS_DATETIME_TIMESTAMP 3 + + #define NGX_RTMP_HLS_SLICING_PLAIN 1 #define NGX_RTMP_HLS_SLICING_ALIGNED 2 @@ -138,6 +145,14 @@ static ngx_conf_enum_t ngx_rtmp_hls_naming_slots[] = { }; +static ngx_conf_enum_t ngx_rtmp_hls_datetime_slots[] = { + { ngx_string("none"), NGX_RTMP_HLS_DATETIME_NONE }, + { ngx_string("system"), NGX_RTMP_HLS_DATETIME_SYSTEM }, + { ngx_string("timestamp"), NGX_RTMP_HLS_DATETIME_TIMESTAMP }, + { ngx_null_string, 0 } +}; + + static ngx_conf_enum_t ngx_rtmp_hls_slicing_slots[] = { { ngx_string("plain"), NGX_RTMP_HLS_SLICING_PLAIN }, { ngx_string("aligned"), NGX_RTMP_HLS_SLICING_ALIGNED }, @@ -224,6 +239,13 @@ static ngx_command_t ngx_rtmp_hls_commands[] = { offsetof(ngx_rtmp_hls_app_conf_t, naming), &ngx_rtmp_hls_naming_slots }, + { ngx_string("hls_datetime"), + 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_hls_app_conf_t, datetime), + &ngx_rtmp_hls_datetime_slots }, + { ngx_string("hls_fragment_slicing"), NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -533,11 +555,7 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s) n = ngx_write_fd(fd, buffer, p - buffer); if (n < 0) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "hls: " ngx_write_fd_n " failed: '%V'", - &ctx->playlist_bak); - ngx_close_file(fd); - return NGX_ERROR; + goto write_err; } sep = hacf->nested ? (hacf->base_url.len ? "/" : "") : "-"; @@ -557,6 +575,21 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s) for (i = 0; i < ctx->nfrags; i++) { f = ngx_rtmp_hls_get_frag(s, i); + if (i == 0 && f->datetime && f->datetime->len > 0) { + p = ngx_snprintf(buffer, sizeof(buffer), "#EXT-X-PROGRAM-DATE-TIME:"); + n = ngx_write_fd(fd, buffer, p - buffer); + if (n < 0) { + goto write_err; + } + n = ngx_write_fd(fd, f->datetime->data, f->datetime->len); + if (n < 0) { + goto write_err; + } + n = ngx_write_fd(fd, "\n", 1); + if (n < 0) { + goto write_err; + } + } p = buffer; end = p + sizeof(buffer); @@ -586,11 +619,7 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s) n = ngx_write_fd(fd, buffer, p - buffer); if (n < 0) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "hls: " ngx_write_fd_n " failed '%V'", - &ctx->playlist_bak); - ngx_close_file(fd); - return NGX_ERROR; + goto write_err; } } @@ -610,6 +639,13 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s) } return NGX_OK; + +write_err: + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "hls: " ngx_write_fd_n " failed '%V'", + &ctx->playlist_bak); + ngx_close_file(fd); + return NGX_ERROR; } @@ -815,6 +851,54 @@ ngx_rtmp_hls_get_fragment_id(ngx_rtmp_session_t *s, uint64_t ts) } +static ngx_str_t * +ngx_rtmp_hls_get_fragment_datetime(ngx_rtmp_session_t *s, uint64_t ts) +{ + ngx_rtmp_hls_ctx_t *ctx; + ngx_rtmp_hls_app_conf_t *hacf; + ngx_str_t *datetime; + ngx_tm_t tm; + uint64_t msec; + + datetime = (ngx_str_t *) ngx_pcalloc(s->connection->pool, sizeof(ngx_str_t)); + datetime->data = NULL; + datetime->len = 0; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); + hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module); + + switch (hacf->datetime) { + + case NGX_RTMP_HLS_DATETIME_TIMESTAMP: + /* Timestamps in RTMP are given as an integer number of milliseconds + * relative to an unspecified epoch, so we clear the last 32 bits + * from system time, and add the timestamp from RTMP. */ + msec = ngx_cached_time->sec * 1000 + ngx_cached_time->msec; + msec /= 4294967296; //2**32 + msec *= 4294967296; + msec += (ts / 90); + ngx_gmtime(msec / 1000, &tm); + + datetime->data = (u_char *) ngx_pcalloc(s->connection->pool, ngx_cached_http_log_iso8601.len * sizeof(u_char)); + (void) ngx_sprintf(datetime->data, "%4d-%02d-%02dT%02d:%02d:%02d-00:00", + tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec); + datetime->len = ngx_cached_http_log_iso8601.len; + return datetime; + + case NGX_RTMP_HLS_DATETIME_SYSTEM: + datetime->data = (u_char *) ngx_pcalloc(s->connection->pool, ngx_cached_http_log_iso8601.len * sizeof(u_char)); + ngx_cpymem(&datetime->data, (const void *) &ngx_cached_http_log_iso8601.data, ngx_cached_http_log_iso8601.len); + datetime->len = ngx_cached_http_log_iso8601.len; + return datetime; + + default: /* NGX_RTMP_HLS_DATETIME_NONE */ + return datetime; + } +} + + static ngx_int_t ngx_rtmp_hls_close_fragment(ngx_rtmp_session_t *s) { @@ -846,6 +930,7 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts, { uint64_t id; ngx_fd_t fd; + ngx_str_t *datetime; ngx_uint_t g; ngx_rtmp_hls_ctx_t *ctx; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -871,6 +956,7 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts, } id = ngx_rtmp_hls_get_fragment_id(s, ts); + datetime = ngx_rtmp_hls_get_fragment_datetime(s, ts); if (hacf->granularity) { g = (ngx_uint_t) hacf->granularity; @@ -963,6 +1049,7 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts, f->discont = discont; f->id = id; f->key_id = ctx->key_id; + f->datetime = datetime; ctx->frag_ts = ts; @@ -2317,6 +2404,7 @@ ngx_rtmp_hls_create_app_conf(ngx_conf_t *cf) conf->continuous = NGX_CONF_UNSET; conf->nested = NGX_CONF_UNSET; conf->naming = NGX_CONF_UNSET_UINT; + conf->datetime = NGX_CONF_UNSET_UINT; conf->slicing = NGX_CONF_UNSET_UINT; conf->type = NGX_CONF_UNSET_UINT; conf->max_audio_delay = NGX_CONF_UNSET_MSEC; @@ -2348,6 +2436,8 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->nested, prev->nested, 0); ngx_conf_merge_uint_value(conf->naming, prev->naming, NGX_RTMP_HLS_NAMING_SEQUENTIAL); + ngx_conf_merge_uint_value(conf->datetime, prev->datetime, + NGX_RTMP_HLS_DATETIME_NONE); ngx_conf_merge_uint_value(conf->slicing, prev->slicing, NGX_RTMP_HLS_SLICING_PLAIN); ngx_conf_merge_uint_value(conf->type, prev->type,