ngx_int_t ngx_http_set_formatted_local_time(ngx_http_request_t *r, ngx_str_t *res, ngx_http_variable_value_t *v) { time_t now; u_char *p; struct tm tm; if (v->not_found || v->len == 0) { res->data = NULL; res->len = 0; return NGX_OK; } now = ngx_time(); ngx_libc_localtime(now, &tm); p = ngx_palloc(r->pool, NGX_HTTP_SET_MISC_FMT_DATE_LEN); if (p == NULL) { return NGX_ERROR; } res->len = strftime((char *) p, NGX_HTTP_SET_MISC_FMT_DATE_LEN, (char *) v->data, &tm); if (res->len == 0) { return NGX_ERROR; } res->data = p; return NGX_OK; }
/* when表示期待过期的时间,它仅表示一天内的秒数;1)如果when表示当天时间秒数,当它合并 到实际时间后,已经超过当前时间,那么就返回when合并到实际时间后的秒数(相对于格林威治 时间1970年1月1日凌晨0点0分0秒到某一时间的秒数);反之,如果合并后的时间早于当前时间,则 返回下一天的同一时刻(当天时刻)的时间。它目前仅具有与expires配置项相关的缓存过期功能 */ time_t ngx_next_time(time_t when) { time_t now, next; struct tm tm; now = ngx_time(); ngx_libc_localtime(now, &tm); tm.tm_hour = (int) (when / 3600); when %= 3600; tm.tm_min = (int) (when / 60); tm.tm_sec = (int) (when % 60); next = mktime(&tm); if (next == -1) { return -1; } if (next - now > 0) { return next; } tm.tm_mday++; /* mktime() should normalize a date (Jan 32, etc) */ next = mktime(&tm); if (next != -1) { return next; } return -1; }
ngx_int_t strftime_now(ngx_http_variable_value_t *var, u_char *date_fmt, ngx_uint_t gmt, ngx_pool_t *pool) { struct tm tm; time_t now = ngx_time(); char buf[DATE_MAX_LEN]; if (var->len != 0) { return NGX_OK; } if (gmt) { ngx_libc_gmtime(now, &tm); } else { ngx_libc_localtime(now, &tm); } var->len = strftime(buf, DATE_MAX_LEN, (char *) date_fmt, &tm); if (var->len == 0) { return NGX_ERROR; } var->data = ngx_palloc(pool, var->len); if (var->data == NULL) { return NGX_ERROR; } ngx_memcpy(var->data, buf, var->len); var->valid = 1; var->no_cacheable = 0; var->not_found = 0; return NGX_OK; }
/* This funcion returns pointer to a static buffer */ static void ngx_rtmp_record_make_path(ngx_rtmp_session_t *s, ngx_rtmp_record_rec_ctx_t *rctx, ngx_str_t *path) { ngx_rtmp_record_ctx_t *ctx; ngx_rtmp_record_app_conf_t *rracf; u_char *p, *l; struct tm tm; static u_char buf[NGX_TIME_T_LEN + 1]; static u_char pbuf[NGX_MAX_PATH + 1]; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_record_module); rracf = rctx->conf; /* create file path */ p = pbuf; l = pbuf + sizeof(pbuf) - 1; p = ngx_cpymem(p, rracf->path.data, ngx_min(rracf->path.len, (size_t)(l - p - 1))); *p++ = '/'; p = (u_char *)ngx_escape_uri(p, ctx->name, ngx_min(ngx_strlen(ctx->name), (size_t)(l - p)), NGX_ESCAPE_URI_COMPONENT); /* append timestamp */ if (rracf->unique) { p = ngx_cpymem(p, buf, ngx_min(ngx_sprintf(buf, "-%T", rctx->timestamp) - buf, l - p)); } if (ngx_strchr(rracf->suffix.data, '%')) { ngx_libc_localtime(rctx->timestamp, &tm); p += strftime((char *) p, l - p, (char *) rracf->suffix.data, &tm); } else { p = ngx_cpymem(p, rracf->suffix.data, ngx_min(rracf->suffix.len, (size_t)(l - p))); } *p = 0; path->data = pbuf; path->len = p - pbuf; ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "record: %V path: '%V'", &rracf->id, path); }
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; struct tm tm; ngx_str_t noname, *name; ngx_uint_t i, frame_rate_num, frame_rate_denom; 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; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; static u_char start_time[sizeof("1970-09-28T12:00:00+06:00")]; static u_char end_time[sizeof("1970-09-28T12:00:00+06:00")]; 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; } #define NGX_RTMP_DASH_MANIFEST_HEADER \ "<?xml version=\"1.0\"?>\n" \ "<MPD\n" \ " type=\"dynamic\"\n" \ " xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" \ " availabilityStartTime=\"%s\"\n" \ " availabilityEndTime=\"%s\"\n" \ " minimumUpdatePeriod=\"PT%uiS\"\n" \ " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ " suggestedPresentationDelay=\"PT%uiS\"\n" \ " profiles=\"urn:hbbtv:dash:profile:isoff-live:2012," \ "urn:mpeg:dash:profile:isoff-live:2011\"\n" \ " xmlns:xsi=\"http://www.w3.org/2011/XMLSchema-instance\"\n" \ " xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 DASH-MPD.xsd\">\n" \ " <Period start=\"PT0S\" id=\"dash\">\n" #define NGX_RTMP_DASH_MANIFEST_VIDEO \ " <AdaptationSet\n" \ " id=\"1\"\n" \ " segmentAlignment=\"true\"\n" \ " maxWidth=\"%ui\"\n" \ " maxHeight=\"%ui\"\n" \ " maxFrameRate=\"%s\">\n" \ " <Representation\n" \ " id=\"%V_H264\"\n" \ " mimeType=\"video/mp4\"\n" \ " codecs=\"avc1.%02uxi%02uxi%02uxi\"\n" \ " width=\"%ui\"\n" \ " height=\"%ui\"\n" \ " frameRate=\"%s\"\n" \ " sar=\"1:1\"\n" \ " startWithSAP=\"1\"\n" \ " bandwidth=\"%ui\">\n" \ " <SegmentTemplate\n" \ " presentationTimeOffset=\"0\"\n" \ " timescale=\"1000\"\n" \ " media=\"%V%s$Time$.m4v\"\n" \ " initialization=\"%V%sinit.m4v\">\n" \ " <SegmentTimeline>\n" #define NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER \ " </SegmentTimeline>\n" \ " </SegmentTemplate>\n" \ " </Representation>\n" \ " </AdaptationSet>\n" #define NGX_RTMP_DASH_MANIFEST_TIME \ " <S t=\"%uD\" d=\"%uD\"/>\n" #define NGX_RTMP_DASH_MANIFEST_AUDIO \ " <AdaptationSet\n" \ " id=\"2\"\n" \ " segmentAlignment=\"true\">\n" \ " <AudioChannelConfiguration\n" \ " schemeIdUri=\"urn:mpeg:dash:" \ "23003:3:audio_channel_configuration:2011\"\n" \ " value=\"1\"/>\n" \ " <Representation\n" \ " id=\"%V_AAC\"\n" \ " mimeType=\"audio/mp4\"\n" \ " codecs=\"mp4a.%s\"\n" \ " audioSamplingRate=\"%ui\"\n" \ " startWithSAP=\"1\"\n" \ " bandwidth=\"%ui\">\n" \ " <SegmentTemplate\n" \ " presentationTimeOffset=\"0\"\n" \ " timescale=\"1000\"\n" \ " media=\"%V%s$Time$.m4a\"\n" \ " initialization=\"%V%sinit.m4a\">\n" \ " <SegmentTimeline>\n" #define NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER \ " </SegmentTimeline>\n" \ " </SegmentTemplate>\n" \ " </Representation>\n" \ " </AdaptationSet>\n" #define NGX_RTMP_DASH_MANIFEST_FOOTER \ " </Period>\n" \ "</MPD>\n" ngx_libc_localtime(ctx->start_time.sec + ngx_rtmp_dash_get_frag(s, 0)->timestamp / 1000, &tm); *ngx_sprintf(start_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ctx->start_time.gmtoff < 0 ? '-' : '+', ngx_abs(ctx->start_time.gmtoff / 60), ngx_abs(ctx->start_time.gmtoff % 60)) = 0; ngx_libc_localtime(ctx->start_time.sec + (ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->timestamp + ngx_rtmp_dash_get_frag(s, ctx->nfrags - 1)->duration) / 1000, &tm); *ngx_sprintf(end_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, ctx->start_time.gmtoff < 0 ? '-' : '+', ngx_abs(ctx->start_time.gmtoff / 60), ngx_abs(ctx->start_time.gmtoff % 60)) = 0; last = buffer + sizeof(buffer); p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, start_time, end_time, (ngx_uint_t) (dacf->fraglen / 1000), (ngx_uint_t) (dacf->fraglen / 1000), (ngx_uint_t) (dacf->fraglen / 500)); 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; } p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_VIDEO, codec_ctx->width, codec_ctx->height, frame_rate, &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_VIDEO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); } if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_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_AUDIO_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); } 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); 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; } return NGX_OK; }