From a5a59aa50c9998b637cdbc6fe181d94f8a7a8e1c Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Mon, 30 Sep 2013 13:58:02 -0400 Subject: [PATCH 01/50] MPEG-DASH Support Please not this is a work in progress. For testing, please set dash_cleanup off;. You can use this player to test. http://dashif.org/reference/players/javascript/1.0.0/index.html If you have a specific player that you need changes to support, please file an issue on github --- config | 3 + hls/ngx_rtmp_dash_module.c | 1284 ++++++++++++++++++++++++++++++++++++ hls/ngx_rtmp_mp4.c | 1064 ++++++++++++++++++++++++++++++ hls/ngx_rtmp_mp4.h | 38 ++ 4 files changed, 2389 insertions(+) create mode 100644 hls/ngx_rtmp_dash_module.c create mode 100644 hls/ngx_rtmp_mp4.c create mode 100644 hls/ngx_rtmp_mp4.h diff --git a/config b/config index 2bdb47f..d82c7d0 100644 --- a/config +++ b/config @@ -20,6 +20,7 @@ CORE_MODULES="$CORE_MODULES ngx_rtmp_log_module \ ngx_rtmp_limit_module \ ngx_rtmp_hls_module \ + ngx_rtmp_dash_module \ " @@ -43,6 +44,7 @@ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ $ngx_addon_dir/ngx_rtmp_relay_module.h \ $ngx_addon_dir/ngx_rtmp_streams.h \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.h \ + $ngx_addon_dir/hls/ngx_rtmp_mp4.h \ " @@ -78,6 +80,7 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/ngx_rtmp_limit_module.c \ $ngx_addon_dir/hls/ngx_rtmp_hls_module.c \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.c \ + $ngx_addon_dir/hls/ngx_rtmp_mp4.c \ " CFLAGS="$CFLAGS -I$ngx_addon_dir" diff --git a/hls/ngx_rtmp_dash_module.c b/hls/ngx_rtmp_dash_module.c new file mode 100644 index 0000000..99794d5 --- /dev/null +++ b/hls/ngx_rtmp_dash_module.c @@ -0,0 +1,1284 @@ +#include +#include +#include +#include +#include "ngx_rtmp_notify_module.h" +#include "ngx_rtmp_live_module.h" +#include "ngx_rtmp_mp4.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_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); + +#define NGX_RTMP_DASH_BUFSIZE (1024*1024) +#define NGX_RTMP_DASH_DIR_ACCESS 0744 +#define NGX_RTMP_DASH_MAX_SIZE (800*1024) + +typedef struct { + ngx_uint_t video_earliest_pres_time; + ngx_uint_t video_latest_pres_time; + ngx_uint_t audio_earliest_pres_time; + ngx_uint_t audio_latest_pres_time; + unsigned SAP:1; + uint32_t id; +} ngx_rtmp_dash_frag_t; + +typedef struct { + ngx_str_t playlist; + ngx_str_t playlist_bak; + ngx_str_t name; + ngx_str_t stream; + ngx_str_t start_time; + + unsigned opened:1; + unsigned video:1; + unsigned audio:1; + + ngx_file_t video_file; + ngx_file_t audio_file; + + uint64_t frag; + uint64_t nfrags; + ngx_rtmp_dash_frag_t *frags; + + ngx_buf_t *buffer; + + ngx_uint_t video_mdat_size; + uint32_t video_sample_count; + uint32_t video_sample_sizes[128]; + ngx_str_t video_fragment; + + ngx_uint_t audio_mdat_size; + uint32_t audio_sample_count; + uint32_t audio_sample_sizes[128]; + ngx_str_t audio_fragment; +} ngx_rtmp_dash_ctx_t; + +typedef struct { + ngx_str_t path; + ngx_msec_t playlen; +} ngx_rtmp_dash_cleanup_t; + +typedef struct { + ngx_flag_t dash; + ngx_msec_t fraglen; + ngx_msec_t playlen; + ngx_str_t path; + ngx_uint_t winfrags; + ngx_flag_t cleanup; + ngx_path_t *slot; +} 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_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 *hacf; + + hacf = 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) % (hacf->winfrags * 2 + 1)]; +} + +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_int_t +ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) +{ + static u_char buffer[2048]; + int fd; + u_char *p; + ngx_rtmp_dash_app_conf_t *hacf; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_live_ctx_t *live_ctx; + ssize_t n; + ngx_str_t playlist, playlist_bak; + //ngx_rtmp_dash_frag_t *f; + + hacf = 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); + live_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); + + if (hacf == NULL || ctx == NULL || codec_ctx == NULL || + live_ctx == NULL || live_ctx->stream == NULL) { + return NGX_ERROR; + } + + /* done playlists */ + + 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 \ + "\n"\ + "\n"\ + " \n" +#define NGX_RTMP_DASH_MANIFEST_VIDEO \ + " \n"\ + " \n"\ + " \n"\ + " \n"\ + " \n" +#define NGX_RTMP_DASH_MANIFEST_AUDIO \ + " \n"\ + " \n"\ + " \n"\ + " \n"\ + " \n"\ + " \n" +#define NGX_RTMP_DASH_MANIFEST_FOOTER \ + " \n"\ + "\n" + + //f = ngx_rtmp_dash_get_frag(s, hacf->winfrags/2); + + p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER, + &ctx->start_time); + n = ngx_write_fd(fd, buffer, p - buffer); + + if (ctx->video) { + p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_VIDEO, + codec_ctx->width, + codec_ctx->height, + codec_ctx->frame_rate, + codec_ctx->width, + codec_ctx->height, + codec_ctx->frame_rate, + (uint32_t)(live_ctx->stream->bw_in.bandwidth*8), + hacf->fraglen, + &ctx->name, + &ctx->name); + n = ngx_write_fd(fd, buffer, p - buffer); + } + + if (ctx->audio) { + p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, + codec_ctx->sample_rate, + codec_ctx->sample_rate, + (uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)), + &ctx->name, + &ctx->name); + n = ngx_write_fd(fd, buffer, p - buffer); + } + + p = ngx_snprintf(buffer, sizeof(buffer), 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_rename_file(ctx->playlist_bak.data, ctx->playlist.data)) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%V'->'%V'", + &playlist_bak, &playlist); + return NGX_ERROR; + } + + return NGX_OK; +} + +static ngx_int_t +ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + int rc; + ngx_file_t file; + static u_char path[1024]; + ngx_buf_t *b; + ngx_rtmp_mp4_metadata_t metadata; + + 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 || !codec_ctx) { + return NGX_ERROR; + } + + ngx_memzero(&path, sizeof(path)); + ngx_str_set(&file.name, "dash-init-video"); + ngx_snprintf(path, sizeof(path), "%Vinit-video.dash",&ctx->stream); + + file.fd = ngx_open_file(path, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating video init file"); + return NGX_ERROR; + } + + file.log = s->connection->log; + + b = ngx_pcalloc(s->connection->pool, sizeof(ngx_buf_t)); + if (b == NULL) { + return NGX_ERROR; + } + b->start = ngx_palloc(s->connection->pool, 1024); + if (b->start == NULL) { + return NGX_ERROR; + } + + b->end = b->start + 1024; + b->pos = b->last = b->start; + + metadata.width = codec_ctx->width; + metadata.height = codec_ctx->height; + metadata.audio = 0; + metadata.video = 1; + metadata.sample_rate = codec_ctx->sample_rate; + metadata.frame_rate = codec_ctx->frame_rate; + + ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_INIT, metadata); + ngx_rtmp_mp4_write_moov(s, b, metadata); + rc = ngx_write_file(&file, b->start, b->last-b->start, 0); + if (rc < 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: writing video init failed"); + } + ngx_close_file(file.fd); + + ngx_memzero(&path, sizeof(path)); + ngx_snprintf(path, sizeof(path), "%Vinit-audio.dash",&ctx->stream); + + file.fd = ngx_open_file(path, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (file.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; + } + + file.log = s->connection->log; + b->pos = b->last = b->start; + + metadata.video = 0; + metadata.audio = 1; + ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_INIT, metadata); + ngx_rtmp_mp4_write_moov(s, b, metadata); + rc = ngx_write_file(&file, b->start, b->last-b->start, 0); + if (rc < 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: writing video init failed"); + } + ngx_close_file(file.fd); + + ctx->buffer = b; + + return NGX_OK; +} + +static ngx_int_t +ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_buf_t *b, file_b; + ssize_t written = 0, size, write_size; + ngx_file_t file; + ngx_int_t rc; + ngx_rtmp_mp4_metadata_t metadata; + u_char *pos, *pos1; + ngx_rtmp_dash_frag_t *f; + + static u_char buffer[4096]; + + 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->opened) { + return NGX_OK; + } + + b = ctx->buffer; + + f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + + /* rewrite video segment */ + ngx_memzero(&buffer, sizeof(buffer)); + file_b.start = buffer; + file_b.end = file_b.start + sizeof(buffer); + file_b.pos = file_b.start; + file_b.last = file_b.pos; + + b->pos = b->last = b->start; + + ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata); + pos = b->last; + b->last += 44; /* leave room for sidx */ + ngx_rtmp_mp4_write_moof(b, f->video_earliest_pres_time, ctx->video_sample_count, + ctx->video_sample_sizes, (ctx->nfrags+ctx->frag),0); + pos1 = b->last; + b->last = pos; + ngx_rtmp_mp4_write_sidx(s, b, ctx->video_mdat_size+8+(pos1-(pos+44)), f->video_earliest_pres_time, + f->video_latest_pres_time,0); + b->last = pos1; + ngx_rtmp_mp4_write_mdat(b, ctx->video_mdat_size+8); + + /* move the data down to make room for the headers */ + size = (ssize_t) ctx->video_mdat_size; + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "v-temp") = 0; + + ngx_memzero(&file, sizeof(file)); + file.log = s->connection->log; + file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating dash temp video file"); + return NGX_ERROR; + } + + ngx_str_set(&file.name, "temp"); + + ctx->video_file.offset = 0; + ngx_rtmp_mp4_write_data(s, &file, b); + do { + file_b.pos = file_b.last = file_b.start; + if ((ssize_t)(written + sizeof(buffer)) > size) { + ngx_read_file(&ctx->video_file, file_b.start, size-written, ctx->video_file.offset); + file_b.last += size-written; + } + else { + ngx_read_file(&ctx->video_file, file_b.start, sizeof(buffer), ctx->video_file.offset); + file_b.last += sizeof(buffer); + } + write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); + if (write_size == 0) { + break; + } + written += write_size; + } while (written < size); + + ngx_close_file(ctx->video_file.fd); + ngx_close_file(file.fd); + rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->video_fragment.data); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%s'->'%s'",ctx->stream.data, ctx->video_fragment.data); + return NGX_ERROR; + } + + /* rewrite audio segment */ + written = 0; + ngx_memzero(&buffer, sizeof(buffer)); + file_b.pos = file_b.last = file_b.start; + b->pos = b->last = b->start; + ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata); + pos = b->last; + b->last += 44; /* leave room for sidx */ + ngx_rtmp_mp4_write_moof(b, f->audio_earliest_pres_time, ctx->audio_sample_count, + ctx->audio_sample_sizes, (ctx->nfrags+ctx->frag), codec_ctx->sample_rate); + pos1 = b->last; + b->last = pos; + ngx_rtmp_mp4_write_sidx(s, b, ctx->audio_mdat_size+8+(pos1-(pos+44)), f->audio_earliest_pres_time, + f->audio_latest_pres_time, codec_ctx->sample_rate); + b->last = pos1; + ngx_rtmp_mp4_write_mdat(b, ctx->audio_mdat_size+8); + + /* move the data down to make room for the headers */ + size = (ssize_t) ctx->audio_mdat_size; + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "a-temp") = 0; + + ngx_memzero(&file, sizeof(file)); + file.log = s->connection->log; + file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating temp audio file"); + return NGX_ERROR; + } + + ngx_str_set(&file.name, "temp"); + + ctx->audio_file.offset = 0; + ngx_rtmp_mp4_write_data(s, &file, b); + do { + file_b.pos = file_b.last = file_b.start; + if ((ssize_t)(written + sizeof(buffer)) > size) { + ngx_read_file(&ctx->audio_file, file_b.start, size-written, ctx->audio_file.offset); + file_b.last += size-written; + } + else { + ngx_read_file(&ctx->audio_file, file_b.start, sizeof(buffer), ctx->audio_file.offset); + file_b.last += sizeof(buffer); + } + write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); + if (write_size == 0) { + break; + } + written += write_size; + } while (written < size); + + ngx_close_file(ctx->audio_file.fd); + ngx_close_file(file.fd); + rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->audio_fragment.data); + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: rename failed: '%s'->'%s'",ctx->stream.data, ctx->video_fragment.data); + return NGX_ERROR; + } + + return NGX_OK; +} + +static ngx_int_t +ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *hacf; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + if (!ctx->opened) { + return NGX_OK; + } + + ngx_rtmp_dash_rewrite_segments(s); + + ctx->opened = 0; + + if (ctx->nfrags == hacf->winfrags) { + ctx->frag++; + } else { + ctx->nfrags++; + } + + ngx_rtmp_dash_write_playlist(s); + + return NGX_OK; +} + +static ngx_int_t +ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_frag_t *f; + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + if (ctx->opened) { + return NGX_OK; + } + + f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + f->id = (ctx->frag+ctx->nfrags); + + ngx_memzero(&ctx->video_file, sizeof(ctx->video_file)); + ngx_memzero(&ctx->audio_file, sizeof(ctx->audio_file)); + + ctx->video_file.log = s->connection->log; + ctx->audio_file.log = s->connection->log; + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4v", f->id) = 0; + *ngx_sprintf(ctx->video_fragment.data + ctx->video_fragment.len, "%uL.m4v", f->id) = 0; + ngx_str_set(&ctx->video_file.name, "dash-v"); + ctx->video_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (ctx->video_file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating video fragment file"); + return NGX_ERROR; + } + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4a", f->id) = 0; + *ngx_sprintf(ctx->audio_fragment.data + ctx->audio_fragment.len, "%uL.m4a", f->id) = 0; + ngx_str_set(&ctx->audio_file.name, "dash-a"); + ctx->audio_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (ctx->audio_file.fd == NGX_INVALID_FILE) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: error creating audio fragment file"); + return NGX_ERROR; + } + + ctx->video_sample_count = 0; + f->video_earliest_pres_time = 0; + ctx->video_mdat_size = 0; + + ctx->audio_sample_count = 0; + f->audio_earliest_pres_time = 0; + ctx->audio_mdat_size = 0; + + ctx->opened = 1; + + return NGX_OK; +} + +static ngx_int_t +ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) +{ + ngx_rtmp_dash_app_conf_t *hacf; + ngx_rtmp_dash_ctx_t *ctx; + u_char *p; + size_t len; + + hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + if (hacf == NULL || !hacf->dash || hacf->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)); + ngx_rtmp_set_ctx(s, ctx, ngx_rtmp_dash_module); + + } + + if (ctx->frags == NULL) { + ctx->frags = ngx_pcalloc(s->connection->pool, + sizeof(ngx_rtmp_dash_frag_t) * + (hacf->winfrags * 2 + 1)); + if (ctx->frags == NULL) { + return NGX_ERROR; + } + } + + 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 = hacf->path.len + 1 + ctx->name.len + sizeof(".mpd"); + + ctx->playlist.data = ngx_palloc(s->connection->pool, len); + p = ngx_cpymem(ctx->playlist.data, hacf->path.data, hacf->path.len); + + if (p[-1] != '/') { + *p++ = '/'; + } + + p = ngx_cpymem(p, ctx->name.data, ctx->name.len); + + /* ctx->stream_path 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_INT64_LEN + + sizeof(".mp4")); + + ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1); + ctx->stream.data[ctx->stream.len - 1] = '-'; + + ctx->video_fragment.len = p - ctx->playlist.data + 1; + ctx->video_fragment.data = ngx_palloc(s->connection->pool, + ctx->video_fragment.len + NGX_INT64_LEN + + sizeof(".m4v")); + ctx->audio_fragment.len = p - ctx->playlist.data + 1; + ctx->audio_fragment.data = ngx_palloc(s->connection->pool, + ctx->audio_fragment.len + NGX_INT64_LEN + + sizeof(".m4a")); + + ngx_memcpy(ctx->video_fragment.data, ctx->playlist.data, ctx->video_fragment.len - 1); + ctx->video_fragment.data[ctx->video_fragment.len - 1] = '-'; + ngx_memcpy(ctx->audio_fragment.data, ctx->playlist.data, ctx->audio_fragment.len - 1); + ctx->audio_fragment.data[ctx->audio_fragment.len - 1] = '-'; + + + /* playlist path */ + 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; + + 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); + + /* start time for mpd */ + ctx->start_time.data = ngx_palloc(s->connection->pool, + ngx_cached_http_log_iso8601.len); + ngx_memcpy(ctx->start_time.data, ngx_cached_http_log_iso8601.data, + ngx_cached_http_log_iso8601.len); + ctx->start_time.len = ngx_cached_http_log_iso8601.len; + +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_app_conf_t *hacf; + ngx_rtmp_dash_ctx_t *ctx; + + hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + if (hacf == NULL || !hacf->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 ts) +{ + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *hacf; + int32_t duration; + ngx_rtmp_dash_frag_t *f; + + hacf = 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); + + duration = ctx->video ? (f->video_latest_pres_time-f->video_earliest_pres_time) : + (f->audio_latest_pres_time-f->audio_earliest_pres_time); + + if ((ctx->video) && ((int32_t)(hacf->fraglen - duration) > 150)) { + boundary = 0; + } + if (!ctx->video && ctx->audio) { + if ((int32_t)(hacf->fraglen - duration) > 0) { + boundary = 0; + } + else { + boundary = 1; + } + } + + if (ctx->nfrags == 0) { + ngx_rtmp_dash_write_init_segments(s); + boundary = 1; + } + + if (boundary) { + f->SAP = 1; + } + + if (ctx->audio_mdat_size >= NGX_RTMP_DASH_MAX_SIZE) { + boundary = 1; + } + if (ctx->video_mdat_size >= NGX_RTMP_DASH_MAX_SIZE) { + boundary = 1; + } + + if (boundary) { + ngx_rtmp_dash_close_fragments(s); + ngx_rtmp_dash_open_fragments(s); + } +} + + +static ngx_int_t +ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_rtmp_dash_app_conf_t *hacf; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + size_t bsize; + ngx_buf_t out; + ngx_rtmp_dash_frag_t *f; + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + + hacf = 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 (hacf == NULL || !hacf->dash || ctx == NULL || + codec_ctx == NULL || h->mlen < 2) + { + return NGX_OK; + } + + if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC || + codec_ctx->aac_header == NULL) + { + return NGX_OK; + } + + ngx_memzero(&out, sizeof(out)); + + out.start = buffer; + out.end = buffer + sizeof(buffer); + out.pos = out.start; + out.last = out.pos; + + /* copy payload */ + + ctx->audio = 1; + + for (; in && out.last < out.end; in = in->next) { + bsize = in->buf->last - in->buf->pos; + if (bsize < 4) { + continue; + } + if (out.last + bsize > out.end) { + bsize = out.end - out.last; + } + if (*in->buf->pos == 0xAF) { /* rtmp frame header */ + if (*(in->buf->pos+1) == 0x00) { /* rtmp audio frame number--skip 0 */ + break; + } + else { + if (bsize > 2) { + in->buf->pos += 2; /* skip two bytes of audio frame header */ + } + } + } + if (!in->next) { + bsize -= 2; /* chop 2 bytes off the end of the frame */ + } + out.last = ngx_cpymem(out.last, in->buf->pos, bsize); + } + + if (out.last-out.pos > 0) { + ngx_rtmp_dash_update_fragments(s, 0, h->timestamp); + + f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + + /* Set Presentation Times */ + if (ctx->audio_sample_count == 0 ) { + f->audio_earliest_pres_time = h->timestamp; + } + f->audio_latest_pres_time = h->timestamp; + + ctx->audio_sample_count += 1; + if ((ctx->audio_sample_count <= sizeof(ctx->audio_sample_sizes))) { + ctx->audio_sample_sizes[ctx->audio_sample_count] = ngx_rtmp_mp4_write_data(s, &ctx->audio_file, &out); + ctx->audio_mdat_size += ctx->audio_sample_sizes[ctx->audio_sample_count]; + } + else { + ctx->audio_sample_count = sizeof(ctx->audio_sample_count); + } + } + + return NGX_OK; +} + +static ngx_int_t +ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_rtmp_dash_app_conf_t *hacf; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + u_char *p; + uint8_t htype, fmt, ftype; + uint32_t i = 1; + ngx_buf_t out; + size_t bsize; + ngx_rtmp_dash_frag_t *f; + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + + hacf = 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 (hacf == NULL || !hacf->dash || ctx == NULL || codec_ctx == NULL || + codec_ctx->avc_header == NULL || h->mlen < 1) + { + return NGX_OK; + } + + /* Only H264 is supported */ + if (codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) { + return NGX_OK; + } + + ctx->video = 1; + + if (in->buf->last-in->buf->pos < 2) { + return NGX_ERROR; + } + + /* 1: keyframe (IDR) + * 2: inter frame + * 3: disposable inter frame */ + + ngx_memcpy(&fmt, in->buf->pos, 1); + ftype = (fmt & 0xf0) >> 4; + + /* proceed only with PICT */ + + ngx_memcpy(&htype, in->buf->pos+1, 1); + if (htype != 1) { + return NGX_OK; + } + + ngx_memzero(&out, sizeof(out)); + out.start = buffer; + out.end = buffer + sizeof(buffer); + out.pos = out.start; + out.last = out.pos; + + ngx_rtmp_dash_update_fragments(s, (ftype ==1), h->timestamp); + + f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + + if (!ctx->opened) { + return NGX_OK; + } + + /* Set presentation times */ + if (ctx->video_sample_count == 0) { + f->video_earliest_pres_time = h->timestamp; + } + f->video_latest_pres_time = h->timestamp; + + for (; in && out.last < out.end; in = in->next) { + p = in->buf->pos; + if (i == 1) { + i = 2; + p += 5; + } + bsize = in->buf->last - p; + if (out.last + bsize > out.end) { + bsize = out.end - out.last; + } + + out.last = ngx_cpymem(out.last, p, bsize); + } + + ctx->video_sample_count += 1; + if (ctx->video_sample_count <= sizeof(ctx->video_sample_sizes)) { + ctx->video_sample_sizes[ctx->video_sample_count] = ngx_rtmp_mp4_write_data(s, &ctx->video_file, &out); + ctx->video_mdat_size += ctx->video_sample_sizes[ctx->video_sample_count]; + } + else { + ctx->video_sample_count = sizeof(ctx->video_sample_count); + } + + return NGX_OK; +} + +static void +ngx_rtmp_dash_discontinue(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) { + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: discontinue"); + + ngx_close_file(ctx->video_file.fd); + ngx_close_file(ctx->audio_file.fd); + ctx->opened = 0; + } +} + +static ngx_int_t +ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) +{ + ngx_rtmp_dash_discontinue(s); + + 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_discontinue(s); + + return next_stream_eof(s, v); +} + +static ngx_int_t +ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) +{ + ngx_dir_t dir; + time_t mtime, max_age; + ngx_err_t err; + ngx_str_t name, spath; + u_char *p; + ngx_int_t nentries, nerased; + u_char path[NGX_MAX_PATH + 1]; + + 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 (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); + + if (ngx_delete_dir(spath.data) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "dash: cleanup dir error '%V'", &spath); + } else { + nerased++; + } + } + + continue; + } + + if (!ngx_de_is_file(&dir)) { + continue; + } + + 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 / 166; + + } 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 / 166; + + } 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 / 166; + } 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(spath.data) != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, + "dash: cleanup error '%V'", &spath); + continue; + } + + nerased++; + } +} + +static time_t +ngx_rtmp_dash_cleanup(void *data) +{ + ngx_rtmp_dash_cleanup_t *cleanup = data; + + ngx_rtmp_dash_cleanup_dir(&cleanup->path, cleanup->playlen); + + return cleanup->playlen / 500; +} + +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; + + 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_str_value(conf->path, prev->path, ""); + ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1); + + if (conf->fraglen) { + conf->winfrags = conf->playlen / conf->fraglen; + } + + /* schedule cleanup */ + + if (conf->path.len == 0 || !conf->cleanup) { + return NGX_CONF_OK; + } + + 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; + } + + return NGX_CONF_OK; +} + +static ngx_int_t +ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) +{ + ngx_rtmp_core_main_conf_t *cmcf; + ngx_rtmp_handler_pt *h; + + 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; + + return NGX_OK; +} \ No newline at end of file diff --git a/hls/ngx_rtmp_mp4.c b/hls/ngx_rtmp_mp4.c new file mode 100644 index 0000000..13fef5a --- /dev/null +++ b/hls/ngx_rtmp_mp4.c @@ -0,0 +1,1064 @@ + +#include +#include +#include "ngx_rtmp_mp4.h" +#include + +static u_char compressor_name[] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 +}; + +ngx_int_t +ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) +{ + u_char bytes[8]; + + bytes[0] = (n >> 56) & 0xFF; + bytes[1] = (n >> 48) & 0xFF; + bytes[2] = (n >> 40) & 0xFF; + bytes[3] = (n >> 32) & 0xFF; + bytes[4] = (n >> 24) & 0xFF; + bytes[5] = (n >> 16) & 0xFF; + bytes[6] = (n >> 8) & 0xFF; + bytes[7] = n & 0xFF; + + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_field_32(ngx_buf_t *b, unsigned long n) +{ + u_char bytes[4]; + + bytes[0] = (n >> 24) & 0xFF; + bytes[1] = (n >> 16) & 0xFF; + bytes[2] = (n >> 8) & 0xFF; + bytes[3] = n & 0xFF; + + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_field_24(ngx_buf_t *b, unsigned long n) +{ + u_char bytes[3]; + + bytes[0] = (n >> 16) & 0xFF; + bytes[1] = (n >> 8) & 0xFF; + bytes[2] = n & 0xFF; + + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_field_16(ngx_buf_t *b, unsigned long n) +{ + u_char bytes[2]; + + bytes[0] = (n >> 8) & 0xFF; + bytes[1] = n & 0xFF; + + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) +{ + u_char bytes[1]; + + bytes[0] = n & 0xFF; + + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { + //int i = 3; + + /* initially stolen from ffmpeg, but most of it doesnt appear to be necessary */ + + ngx_rtmp_mp4_field_8(b, tag); + //for (; i > 0; i--) { + // ngx_rtmp_mp4_field_8(b, (size >> (7 * i)) | 0x80); + //} + ngx_rtmp_mp4_field_8(b, size & 0x7F); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) +{ + u_char *curpos; + + curpos = b->last; + b->last = pos; + + ngx_rtmp_mp4_field_32(b, (curpos-pos)); + + b->last = curpos; + + return NGX_OK; +} + +/* transformation matrix + |a b u| + |c d v| + |tx ty w| */ +ngx_int_t +ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, + int16_t d, int16_t tx, int16_t ty) +{ + ngx_rtmp_mp4_field_32(buf, a << 16); /* 16.16 format */ + ngx_rtmp_mp4_field_32(buf, b << 16); /* 16.16 format */ + ngx_rtmp_mp4_field_32(buf, 0); /* u in 2.30 format */ + ngx_rtmp_mp4_field_32(buf, c << 16); /* 16.16 format */ + ngx_rtmp_mp4_field_32(buf, d << 16); /* 16.16 format */ + ngx_rtmp_mp4_field_32(buf, 0); /* v in 2.30 format */ + ngx_rtmp_mp4_field_32(buf, tx << 16); /* 16.16 format */ + ngx_rtmp_mp4_field_32(buf, ty << 16); /* 16.16 format */ + ngx_rtmp_mp4_field_32(buf, 1 << 30); /* w in 2.30 format */ + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + if (type == NGX_RTMP_MP4_FILETYPE_INIT) { + b->last = ngx_cpymem(b->last, "ftypiso5", sizeof("ftypiso5")-1); + + ngx_rtmp_mp4_field_32(b, 1); + if (metadata.video == 1) { + b->last = ngx_cpymem(b->last, "avc1iso5dash", sizeof("avc1iso5dash")-1); + } + else { + b->last = ngx_cpymem(b->last, "iso5dash", sizeof("iso5dash")-1); + } + + } + if (type == NGX_RTMP_MP4_FILETYPE_SEG) { + b->last = ngx_cpymem(b->last, "stypmsdh", sizeof("stypmsdh")-1); + + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "msdhmsix", sizeof("msdhmsix")-1); + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "mvhd", sizeof("mvhd")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_field_32(b, 0x00000000); /* creation time */ + ngx_rtmp_mp4_field_32(b, 0); /* modification time */ + ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); /* timescale */ + ngx_rtmp_mp4_field_32(b, 0); /* duration */ + ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_PREFERRED_RATE); /* preferred playback rate */ + ngx_rtmp_mp4_field_16(b, NGX_RTMP_MP4_PREFERRED_VOLUME); /* preferred volume rate */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + + ngx_rtmp_mp4_write_matrix(b, 1, 0, 0, 1, 0, 0); + + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + + ngx_rtmp_mp4_field_32(b, 1); /* track id */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "tkhd", sizeof("tkhd")-1); + + ngx_rtmp_mp4_field_8(b, 0); /* version */ + ngx_rtmp_mp4_field_24(b, 0x0000000F); /* flags */ + ngx_rtmp_mp4_field_32(b, 0); /* creation time */ + ngx_rtmp_mp4_field_32(b, 0); /* modification time */ + ngx_rtmp_mp4_field_32(b, 1); /* track id */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* duration */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0x00000001 : 0); /* 2 16s, layer and alternate group */ + ngx_rtmp_mp4_field_16(b, metadata.audio == 1 ? 0x0100 : 0); + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + + ngx_rtmp_mp4_write_matrix(b, 1, 0, 0, 1, 0, 0); + + if (metadata.video == 1) { + ngx_rtmp_mp4_field_32(b, metadata.width << 16); /* width */ + ngx_rtmp_mp4_field_32(b, metadata.height << 16); /* height */ + } + else { + ngx_rtmp_mp4_field_32(b, 0); /* not relevant for audio */ + ngx_rtmp_mp4_field_32(b, 0); /* not relevant for audio */ + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "mdhd", sizeof("mdhd")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_field_32(b, 0); /* creation time */ + ngx_rtmp_mp4_field_32(b, 0); /* modification time */ + ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? metadata.sample_rate : NGX_RTMP_MP4_TIMESCALE); /* time scale*/ + ngx_rtmp_mp4_field_32(b, 0); /* duration */ + ngx_rtmp_mp4_field_16(b, 0x15C7); /* language */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "hdlr", sizeof("hdlr")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0); /* pre defined (=0) */ + if (metadata.video == 1) { + b->last = ngx_cpymem(b->last, "vide", sizeof("vide")-1); /* video handler */ + } + else { + b->last = ngx_cpymem(b->last, "soun", sizeof("soun")-1); /* sound handler */ + } + + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + + if (metadata.video == 1) { + b->last = ngx_cpymem(b->last, "VideoHandler", sizeof("VideoHandler")); /* video handler string--NULL TERMINATED */ + } + else { + b->last = ngx_cpymem(b->last, "SoundHandler", sizeof("SoundHandler")); /* sound handler string--NULL TERMINATED */ + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_vmhd(ngx_buf_t *b) +{ + /* size is always 0x14, apparently */ + ngx_rtmp_mp4_field_32(b, 0x14); + + b->last = ngx_cpymem(b->last, "vmhd", sizeof("vmhd")-1); + + ngx_rtmp_mp4_field_32(b, 0x01); /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved (graphics mode = copy) */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved (graphics mode = copy) */ + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) +{ + /* size is always 16, apparently */ + ngx_rtmp_mp4_field_32(b, 16); + + b->last = ngx_cpymem(b->last, "smhd", sizeof("smhd")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version and flags */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved (balance, normally = 0) */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "dref", sizeof("dref")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ + ngx_rtmp_mp4_field_32(b, 1); /* entry count */ + ngx_rtmp_mp4_field_32(b, 0xc); /* size of url */ + + b->last = ngx_cpymem(b->last, "url ", sizeof("url ")-1); + + ngx_rtmp_mp4_field_32(b, 0x00000001); /* version & flags */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "dinf", sizeof("dinf")-1); + + ngx_rtmp_mp4_write_dref(b, metadata); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos, *p; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_chain_t *in; + + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (codec_ctx == NULL) { + return NGX_ERROR; + } + + in = codec_ctx->avc_header; + if (in == NULL) { + return NGX_ERROR; + } + + pos = b->last; + + for (p=in->buf->pos;p<=in->buf->last;p++) { + if (*p == 0x01) { /* check for start code */ + break; + } + } + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "avcC", sizeof("avcC")-1); + + if (in->buf->last-p > 0) { + b->last = ngx_cpymem(b->last, p, in->buf->last-p); + } + else { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "hls: dash: invalid avcc received"); + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "avc1", sizeof("avc1")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, 1); /* data reference index */ + ngx_rtmp_mp4_field_16(b, 0); /* codec stream version */ + ngx_rtmp_mp4_field_16(b, 0); /* codec stream revision (=0) */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, metadata.width); + ngx_rtmp_mp4_field_16(b, metadata.height); + ngx_rtmp_mp4_field_32(b, 0x00480000); /* Horizontal resolution 72dpi */ + ngx_rtmp_mp4_field_32(b, 0x00480000); /* Vertical resolution 72dpi */ + ngx_rtmp_mp4_field_32(b, 0); /* Data size (= 0) */ + ngx_rtmp_mp4_field_16(b, 1); /* Frame count (= 1) */ + ngx_rtmp_mp4_field_8(b, 0); /* compressor name len */ + + b->last = ngx_cpymem(b->last, compressor_name, sizeof(compressor_name)+1); + + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, 0x18); /* reserved */ + ngx_rtmp_mp4_field_16(b, 0xffff); /* reserved */ + + ngx_rtmp_mp4_write_avcc(s, b, metadata); + + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) +{ + /* SCREW THIS FUNCTION */ + + u_char *pos; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_chain_t *aac; + int decoder_info; + int aac_header_offset; + + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + + if (codec_ctx == NULL) { + return NGX_ERROR; + } + + aac = codec_ctx->aac_header; + if (aac == NULL) { + decoder_info = 0; + aac_header_offset = 0; + } + else { + decoder_info = (aac->buf->last-aac->buf->pos)-2; + aac_header_offset = 2; + } + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "esds", sizeof("esds")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_put_descr(b, 0x03, 23+decoder_info); /* length of the rest of the box */ + ngx_rtmp_mp4_field_16(b, 1); /* track id */ + ngx_rtmp_mp4_field_8(b, 0x00); /* flags */ + ngx_rtmp_mp4_put_descr(b, 0x04, 15+decoder_info); /* length of the rest of the box */ + ngx_rtmp_mp4_field_8(b, 0x40); /* codec id */ + ngx_rtmp_mp4_field_8(b, 0x15); /* audio stream */ + ngx_rtmp_mp4_field_24(b, 0); /* buffersize? */ + ngx_rtmp_mp4_field_32(b, 0x0001F151); /* bitrate TODO: should probably set it dynamically*/ + ngx_rtmp_mp4_field_32(b, 0x0001F14D); /* I really dont know */ + + if (aac) { + ngx_rtmp_mp4_put_descr(b, 0x05, decoder_info); + b->last = ngx_cpymem(b->last, aac->buf->pos+aac_header_offset, decoder_info); + } + + ngx_rtmp_mp4_put_descr(b, 0x06, 1); + ngx_rtmp_mp4_field_8(b, 0x02); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "mp4a", sizeof("mp4a")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, 1); /* Data-reference index, XXX == 1 */ + ngx_rtmp_mp4_field_16(b, 0); /* Version */ + ngx_rtmp_mp4_field_16(b, 0); /* Revision level */ + ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, 2); /* something mp4 specific */ + ngx_rtmp_mp4_field_16(b, 16); /* something mp4 specific */ + ngx_rtmp_mp4_field_16(b, 0); /* something mp4 specific */ + ngx_rtmp_mp4_field_16(b, 0); /* packet size (=0) */ + ngx_rtmp_mp4_field_16(b, metadata.sample_rate); /* sample rate */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + + ngx_rtmp_mp4_write_esds(s, b); + + ngx_rtmp_mp4_field_32(b, 8); /* size */ + ngx_rtmp_mp4_field_32(b, 0); /* null tag */ + + ngx_rtmp_mp4_update_box_size(b, pos); + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "stsd", sizeof("stsd")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ + ngx_rtmp_mp4_field_32(b, 1); /* entry count */ + + if (metadata.video == 1) { + ngx_rtmp_mp4_write_video(s,b,metadata); + } + else { + ngx_rtmp_mp4_write_audio(s,b,metadata); + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_stts(ngx_buf_t *b) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "stts", sizeof("stts")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_field_32(b, 0); /* entry count */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "stsc", sizeof("stsc")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_field_32(b, 0); /* entry count */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "stsz", sizeof("stsz")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_field_32(b, 0); /* entry count */ + ngx_rtmp_mp4_field_32(b, 0); /* moar zeros */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_stco(ngx_buf_t *b) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "stco", sizeof("stco")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_field_32(b, 0); /* entry count */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "stbl", sizeof("stbl")-1); + + ngx_rtmp_mp4_write_stsd(s, b, metadata); + ngx_rtmp_mp4_write_stts(b); + ngx_rtmp_mp4_write_stsc(b); + ngx_rtmp_mp4_write_stsz(b); + ngx_rtmp_mp4_write_stco(b); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "minf", sizeof("minf")-1); + + if (metadata.video == 1) { + ngx_rtmp_mp4_write_vmhd(b); + } + else { + ngx_rtmp_mp4_write_smhd(b); + } + + ngx_rtmp_mp4_write_dinf(b, metadata); + ngx_rtmp_mp4_write_stbl(s, b, metadata); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "mdia", sizeof("mdia")-1); + + ngx_rtmp_mp4_write_mdhd(b, metadata); + ngx_rtmp_mp4_write_hdlr(b, metadata); + ngx_rtmp_mp4_write_minf(s, b, metadata); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "trak", sizeof("trak")-1); + + ngx_rtmp_mp4_write_tkhd(b, metadata); + ngx_rtmp_mp4_write_mdia(s, b, metadata); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + uint32_t sample_dur; + + if (metadata.video == 1) { + sample_dur = metadata.frame_rate > 0 ? NGX_RTMP_MP4_TIMESCALE/metadata.frame_rate : NGX_RTMP_MP4_TIMESCALE; + } + else { + sample_dur = 1024; + } + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "mvex", sizeof("mvex")-1); + + /* just write the trex and mehd in here too */ + + ngx_rtmp_mp4_field_32(b, 16); + + b->last = ngx_cpymem(b->last, "mehd", sizeof("mehd")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ + ngx_rtmp_mp4_field_32(b, 0x000D8D2A); /* frag duration */ + + ngx_rtmp_mp4_field_32(b, 0x20); + + b->last = ngx_cpymem(b->last, "trex", sizeof("trex")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ + ngx_rtmp_mp4_field_32(b, 1); /* track id */ + ngx_rtmp_mp4_field_32(b, 1); /* default sample description index */ + ngx_rtmp_mp4_field_32(b, sample_dur); /* default sample duration */ + ngx_rtmp_mp4_field_32(b, 0); /* default sample size */ + ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0 : 0x00010000); /* default sample flags */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "moov", sizeof("moov")-1); + + ngx_rtmp_mp4_write_mvhd(b, metadata); + ngx_rtmp_mp4_write_mvex(b, metadata); + ngx_rtmp_mp4_write_trak(s, b, metadata); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "tfhd", sizeof("tfhd")-1); + + ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00020020 : 0x00020000); /* version & flags */ + ngx_rtmp_mp4_field_32(b, 1); /* track id */ + if (sample_rate > 0) { + ngx_rtmp_mp4_field_32(b, 0x02000000); + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, ngx_uint_t sample_rate) +{ + u_char *pos; + float multiplier; + + if (sample_rate > 0) { + multiplier = (float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE; + } + else { + multiplier = 1; + } + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "tfdt", sizeof("tfdt")-1); + + ngx_rtmp_mp4_field_32(b, 0x00000000); /* version == 1 aka 64 bit integer */ + ngx_rtmp_mp4_field_32(b, (uint32_t)((float)earliest_pres_time*multiplier)); /* no idea */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, uint32_t sample_sizes[128], + u_char *moof_pos, ngx_uint_t sample_rate) +{ + u_char *pos; + uint32_t i, offset; + + pos = b->last; + + if (sample_rate > 0) { + offset = (pos-moof_pos) + 20 + (sample_count*4) + 8; /* moof stuff + trun stuff + sample entries + mdat header */ + } + else { + offset = (pos-moof_pos) + 24 + (sample_count*4) + 8; /* moof stuff + trun stuff + sample entries + mdat header */ + } + + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "trun", sizeof("trun")-1); + + ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00000201 : 0x00000205); /* version and flags */ + ngx_rtmp_mp4_field_32(b, sample_count); /* sample count */ + ngx_rtmp_mp4_field_32(b, offset); /* data offset */ + if (sample_rate == 0) { + ngx_rtmp_mp4_field_32(b, 0); /* first sample flags */ + } + + for (i = 1; i <= sample_count; i++) { + ngx_rtmp_mp4_field_32(b, sample_sizes[i]); + } + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sample_count, uint32_t sample_sizes[128], + u_char *moof_pos, ngx_uint_t sample_rate) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "traf", sizeof("traf")-1); + + ngx_rtmp_mp4_write_tfhd(b, sample_rate); + ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time, sample_rate); + ngx_rtmp_mp4_write_trun(b, sample_count, sample_sizes, moof_pos, sample_rate); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "mfhd", sizeof("mfhd")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* don't know what this is */ + ngx_rtmp_mp4_field_32(b, index); /* fragment index. */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t reference_size, + ngx_uint_t earliest_pres_time, ngx_uint_t latest_pres_time, ngx_uint_t sample_rate) +{ + u_char *pos; + uint32_t ept, dur; + + if (sample_rate > 0) { + ept = (uint32_t)((float)earliest_pres_time*((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); + dur = (uint32_t)((float)(latest_pres_time-earliest_pres_time)*((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); + } + else { + ept = earliest_pres_time; + dur = (latest_pres_time-earliest_pres_time); + } + + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: buffered dash range start: %uL, duration: %uL", + ept, dur); + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "sidx", sizeof("sidx")-1); + + ngx_rtmp_mp4_field_32(b, 0); /* version */ + ngx_rtmp_mp4_field_32(b, 1); /* reference id */ + ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? sample_rate : NGX_RTMP_MP4_TIMESCALE); /* timescale */ + ngx_rtmp_mp4_field_32(b, ept); /* earliest presentation time */ + ngx_rtmp_mp4_field_32(b, 0); /* first offset */ + ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + ngx_rtmp_mp4_field_16(b, 1); /* reference count (=1) */ + ngx_rtmp_mp4_field_32(b, reference_size); /* 1st bit is reference type, the rest is reference size */ + ngx_rtmp_mp4_field_32(b, dur); /* subsegment duration */ + ngx_rtmp_mp4_field_8(b, 0x90); /* first bit is startsWithSAP (=1), next 3 bits are SAP type (=001) */ + ngx_rtmp_mp4_field_24(b, 0); /* SAP delta time */ + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_int_t +ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sample_count, + uint32_t sample_sizes[128], uint32_t index, ngx_uint_t sample_rate) +{ + u_char *pos; + + pos = b->last; + + /* box size placeholder */ + ngx_rtmp_mp4_field_32(b, 0); + + b->last = ngx_cpymem(b->last, "moof", sizeof("moof")-1); + + ngx_rtmp_mp4_write_mfhd(b, index); + ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, sample_sizes, pos, sample_rate); + + ngx_rtmp_mp4_update_box_size(b, pos); + + return NGX_OK; +} + +ngx_uint_t +ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) +{ + ngx_rtmp_mp4_field_32(b, size); + + b->last = ngx_cpymem(b->last, "mdat", sizeof("mdat")-1); + + return NGX_OK; +} + +uint32_t +ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, ngx_buf_t *b) +{ + ngx_int_t rc; + uint32_t size; + + if (b == NULL) { + return 0; //error + } + + size = b->last-b->start; + if (size < 1) { + return 0; + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: data written: %d", + (int) size); + + rc = ngx_write_file(file, b->start, size, file->offset); + if (rc < 0) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: writing file failed"); + return 0; //error + } + + return size; +} \ No newline at end of file diff --git a/hls/ngx_rtmp_mp4.h b/hls/ngx_rtmp_mp4.h new file mode 100644 index 0000000..0cd2b51 --- /dev/null +++ b/hls/ngx_rtmp_mp4.h @@ -0,0 +1,38 @@ + +#ifndef _NGX_RTMP_MP4_H_INCLUDED_ +#define _NGX_RTMP_MP4_H_INCLUDED_ + + +#include +#include +#include + +typedef struct { + ngx_uint_t width; + ngx_uint_t height; + ngx_uint_t audio; + ngx_uint_t video; + ngx_uint_t sample_rate; + ngx_uint_t frame_rate; +} ngx_rtmp_mp4_metadata_t; + +enum { + NGX_RTMP_MP4_FILETYPE_INIT = 0, + NGX_RTMP_MP4_FILETYPE_SEG = 1 +}; + +#define NGX_RTMP_MP4_TIMESCALE 1000 /* divide all times by this value. this is the same resolution as RTMP so it is convenient */ +#define NGX_RTMP_MP4_PREFERRED_RATE 0x00010000 /* normal forward playback as defined by spec */ +#define NGX_RTMP_MP4_PREFERRED_VOLUME 0x0100 /* full volume as defined by spec */ + +ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t metadata); +ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata); +ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sample_count, + uint32_t sample_sizes[128], uint32_t index, ngx_uint_t sample_rate); +ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t reference_size, + ngx_uint_t earliest_pres_time, ngx_uint_t latest_pres_time, ngx_uint_t sample_rate); +ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); +uint32_t ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, ngx_buf_t *b); + + +#endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ \ No newline at end of file From 2060dccbf6c34dd1d86b324c357c2dd1f1cea56c Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Mon, 30 Sep 2013 16:27:31 -0400 Subject: [PATCH 02/50] Better support for variable keyframe intervals --- config | 1 + hls/ngx_rtmp_dash_module.c | 41 +++++++++++++++++++++----------------- 2 files changed, 24 insertions(+), 18 deletions(-) diff --git a/config b/config index d82c7d0..e6455b2 100644 --- a/config +++ b/config @@ -79,6 +79,7 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/ngx_rtmp_log_module.c \ $ngx_addon_dir/ngx_rtmp_limit_module.c \ $ngx_addon_dir/hls/ngx_rtmp_hls_module.c \ + $ngx_addon_dir/hls/ngx_rtmp_dash_module.c \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.c \ $ngx_addon_dir/hls/ngx_rtmp_mp4.c \ " diff --git a/hls/ngx_rtmp_dash_module.c b/hls/ngx_rtmp_dash_module.c index 99794d5..091d8c5 100644 --- a/hls/ngx_rtmp_dash_module.c +++ b/hls/ngx_rtmp_dash_module.c @@ -2,7 +2,6 @@ #include #include #include -#include "ngx_rtmp_notify_module.h" #include "ngx_rtmp_live_module.h" #include "ngx_rtmp_mp4.h" @@ -19,6 +18,7 @@ static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, #define NGX_RTMP_DASH_BUFSIZE (1024*1024) #define NGX_RTMP_DASH_DIR_ACCESS 0744 #define NGX_RTMP_DASH_MAX_SIZE (800*1024) +#define NGX_RTMP_DASH_MAX_SAMPLES 512 typedef struct { ngx_uint_t video_earliest_pres_time; @@ -43,20 +43,20 @@ typedef struct { ngx_file_t video_file; ngx_file_t audio_file; - uint64_t frag; - uint64_t nfrags; + uint32_t frag; + uint32_t nfrags; ngx_rtmp_dash_frag_t *frags; ngx_buf_t *buffer; ngx_uint_t video_mdat_size; uint32_t video_sample_count; - uint32_t video_sample_sizes[128]; + uint32_t video_sample_sizes[NGX_RTMP_DASH_MAX_SAMPLES]; ngx_str_t video_fragment; ngx_uint_t audio_mdat_size; uint32_t audio_sample_count; - uint32_t audio_sample_sizes[128]; + uint32_t audio_sample_sizes[NGX_RTMP_DASH_MAX_SAMPLES]; ngx_str_t audio_fragment; } ngx_rtmp_dash_ctx_t; @@ -182,7 +182,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_live_ctx_t *live_ctx; ssize_t n; ngx_str_t playlist, playlist_bak; - //ngx_rtmp_dash_frag_t *f; + ngx_rtmp_dash_frag_t *f; + uint32_t audio_dur; hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -227,7 +228,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n"\ "\n" - //f = ngx_rtmp_dash_get_frag(s, hacf->winfrags/2); + f = ngx_rtmp_dash_get_frag(s, hacf->winfrags/2); + + audio_dur = f->id > 0 ? (uint32_t)(codec_ctx->sample_rate*((f->video_latest_pres_time/f->id)/1000)) : + (uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)); p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER, &ctx->start_time); @@ -242,7 +246,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->height, codec_ctx->frame_rate, (uint32_t)(live_ctx->stream->bw_in.bandwidth*8), - hacf->fraglen, + f->id > 0 ? (uint32_t)(f->video_latest_pres_time/f->id) : hacf->fraglen, &ctx->name, &ctx->name); n = ngx_write_fd(fd, buffer, p - buffer); @@ -252,7 +256,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, codec_ctx->sample_rate, codec_ctx->sample_rate, - (uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)), + audio_dur, &ctx->name, &ctx->name); n = ngx_write_fd(fd, buffer, p - buffer); @@ -524,7 +528,7 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->audio_fragment.data); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%s'->'%s'",ctx->stream.data, ctx->video_fragment.data); + "dash: rename failed: '%s'->'%s'",ctx->stream.data, ctx->audio_fragment.data); return NGX_ERROR; } @@ -579,11 +583,11 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) ctx->audio_file.log = s->connection->log; *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4v", f->id) = 0; - *ngx_sprintf(ctx->video_fragment.data + ctx->video_fragment.len, "%uL.m4v", f->id) = 0; + *ngx_sprintf(ctx->video_fragment.data + ctx->stream.len, "%uL.m4v", f->id) = 0; + ngx_str_set(&ctx->video_file.name, "dash-v"); ctx->video_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - if (ctx->video_file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: error creating video fragment file"); @@ -591,7 +595,7 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) } *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4a", f->id) = 0; - *ngx_sprintf(ctx->audio_fragment.data + ctx->audio_fragment.len, "%uL.m4a", f->id) = 0; + *ngx_sprintf(ctx->audio_fragment.data + ctx->stream.len, "%uL.m4a", f->id) = 0; ngx_str_set(&ctx->audio_file.name, "dash-a"); ctx->audio_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -652,6 +656,8 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) if (ctx->frags == NULL) { return NGX_ERROR; } + ctx->frag = 0; + ctx->nfrags = 0; } if (ngx_strstr(v->name, "..")) { @@ -706,7 +712,6 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ngx_memcpy(ctx->audio_fragment.data, ctx->playlist.data, ctx->audio_fragment.len - 1); ctx->audio_fragment.data[ctx->audio_fragment.len - 1] = '-'; - /* playlist path */ p = ngx_cpymem(p, ".mpd", sizeof(".mpd") - 1); @@ -893,12 +898,12 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, f->audio_latest_pres_time = h->timestamp; ctx->audio_sample_count += 1; - if ((ctx->audio_sample_count <= sizeof(ctx->audio_sample_sizes))) { + if ((ctx->audio_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES)) { ctx->audio_sample_sizes[ctx->audio_sample_count] = ngx_rtmp_mp4_write_data(s, &ctx->audio_file, &out); ctx->audio_mdat_size += ctx->audio_sample_sizes[ctx->audio_sample_count]; } else { - ctx->audio_sample_count = sizeof(ctx->audio_sample_count); + ctx->audio_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; } } @@ -992,12 +997,12 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } ctx->video_sample_count += 1; - if (ctx->video_sample_count <= sizeof(ctx->video_sample_sizes)) { + if (ctx->video_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES) { ctx->video_sample_sizes[ctx->video_sample_count] = ngx_rtmp_mp4_write_data(s, &ctx->video_file, &out); ctx->video_mdat_size += ctx->video_sample_sizes[ctx->video_sample_count]; } else { - ctx->video_sample_count = sizeof(ctx->video_sample_count); + ctx->video_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; } return NGX_OK; From 857eca9a6158e6a324dddc9d2ea2150070ea0619 Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Mon, 30 Sep 2013 17:56:37 -0400 Subject: [PATCH 03/50] fix precision on duration --- hls/ngx_rtmp_dash_module.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/hls/ngx_rtmp_dash_module.c b/hls/ngx_rtmp_dash_module.c index 091d8c5..c30a381 100644 --- a/hls/ngx_rtmp_dash_module.c +++ b/hls/ngx_rtmp_dash_module.c @@ -230,7 +230,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) f = ngx_rtmp_dash_get_frag(s, hacf->winfrags/2); - audio_dur = f->id > 0 ? (uint32_t)(codec_ctx->sample_rate*((f->video_latest_pres_time/f->id)/1000)) : + audio_dur = f->id > 0 ? (uint32_t)(codec_ctx->sample_rate*((float)(f->video_latest_pres_time/f->id)/1000.0)) : (uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)); p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER, From 05548c515f336cb992a82b6dd02bb96eb2428758 Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Tue, 1 Oct 2013 10:31:02 -0400 Subject: [PATCH 04/50] added the presentation time offset back in --- hls/ngx_rtmp_dash_module.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/hls/ngx_rtmp_dash_module.c b/hls/ngx_rtmp_dash_module.c index c30a381..31f6a44 100644 --- a/hls/ngx_rtmp_dash_module.c +++ b/hls/ngx_rtmp_dash_module.c @@ -209,19 +209,19 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) #define NGX_RTMP_DASH_MANIFEST_HEADER \ "\n"\ - "\n"\ - " \n" + "\n"\ + " \n" #define NGX_RTMP_DASH_MANIFEST_VIDEO \ " \n"\ " \n"\ - " \n"\ + " \n"\ " \n"\ " \n" #define NGX_RTMP_DASH_MANIFEST_AUDIO \ " \n"\ " \n"\ " \n"\ - " \n"\ + " \n"\ " \n"\ " \n" #define NGX_RTMP_DASH_MANIFEST_FOOTER \ @@ -246,6 +246,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->height, codec_ctx->frame_rate, (uint32_t)(live_ctx->stream->bw_in.bandwidth*8), + f->video_earliest_pres_time, f->id > 0 ? (uint32_t)(f->video_latest_pres_time/f->id) : hacf->fraglen, &ctx->name, &ctx->name); @@ -255,6 +256,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->audio) { p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, codec_ctx->sample_rate, + (uint32_t)(f->audio_earliest_pres_time*((float)codec_ctx->sample_rate/1000.0)), codec_ctx->sample_rate, audio_dur, &ctx->name, From 0ac369cd5acc9c2d592d5035e453de8ca118f0d1 Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Tue, 8 Oct 2013 15:30:55 -0400 Subject: [PATCH 05/50] Explict cast on MP4 writer fuctions --- hls/ngx_rtmp_mp4.c | 40 ++++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/hls/ngx_rtmp_mp4.c b/hls/ngx_rtmp_mp4.c index 13fef5a..076e99c 100644 --- a/hls/ngx_rtmp_mp4.c +++ b/hls/ngx_rtmp_mp4.c @@ -13,14 +13,14 @@ ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) { u_char bytes[8]; - bytes[0] = (n >> 56) & 0xFF; - bytes[1] = (n >> 48) & 0xFF; - bytes[2] = (n >> 40) & 0xFF; - bytes[3] = (n >> 32) & 0xFF; - bytes[4] = (n >> 24) & 0xFF; - bytes[5] = (n >> 16) & 0xFF; - bytes[6] = (n >> 8) & 0xFF; - bytes[7] = n & 0xFF; + bytes[0] = ((uint64_t)n >> 56) & 0xFF; + bytes[1] = ((uint64_t)n >> 48) & 0xFF; + bytes[2] = ((uint64_t)n >> 40) & 0xFF; + bytes[3] = ((uint64_t)n >> 32) & 0xFF; + bytes[4] = ((uint64_t)n >> 24) & 0xFF; + bytes[5] = ((uint64_t)n >> 16) & 0xFF; + bytes[6] = ((uint64_t)n >> 8) & 0xFF; + bytes[7] = (uint64_t)n & 0xFF; b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); @@ -28,14 +28,14 @@ ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) } ngx_int_t -ngx_rtmp_mp4_field_32(ngx_buf_t *b, unsigned long n) +ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) { u_char bytes[4]; - bytes[0] = (n >> 24) & 0xFF; - bytes[1] = (n >> 16) & 0xFF; - bytes[2] = (n >> 8) & 0xFF; - bytes[3] = n & 0xFF; + bytes[0] = ((uint32_t)n >> 24) & 0xFF; + bytes[1] = ((uint32_t)n >> 16) & 0xFF; + bytes[2] = ((uint32_t)n >> 8) & 0xFF; + bytes[3] = (uint32_t)n & 0xFF; b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); @@ -43,13 +43,13 @@ ngx_rtmp_mp4_field_32(ngx_buf_t *b, unsigned long n) } ngx_int_t -ngx_rtmp_mp4_field_24(ngx_buf_t *b, unsigned long n) +ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) { u_char bytes[3]; - bytes[0] = (n >> 16) & 0xFF; - bytes[1] = (n >> 8) & 0xFF; - bytes[2] = n & 0xFF; + bytes[0] = ((uint32_t)n >> 16) & 0xFF; + bytes[1] = ((uint32_t)n >> 8) & 0xFF; + bytes[2] = (uint32_t)n & 0xFF; b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); @@ -57,12 +57,12 @@ ngx_rtmp_mp4_field_24(ngx_buf_t *b, unsigned long n) } ngx_int_t -ngx_rtmp_mp4_field_16(ngx_buf_t *b, unsigned long n) +ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) { u_char bytes[2]; - bytes[0] = (n >> 8) & 0xFF; - bytes[1] = n & 0xFF; + bytes[0] = ((uint32_t)n >> 8) & 0xFF; + bytes[1] = (uint32_t)n & 0xFF; b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); From 055228cc8af3d637544a8c70de89ed22dcceb9cb Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Tue, 8 Oct 2013 16:15:44 -0400 Subject: [PATCH 06/50] style changes --- hls/ngx_rtmp_dash_module.c | 202 ++++++++++++++++++++++++------------- hls/ngx_rtmp_mp4.c | 183 ++++++++++++++++++++++++--------- hls/ngx_rtmp_mp4.h | 37 +++++-- 3 files changed, 294 insertions(+), 128 deletions(-) diff --git a/hls/ngx_rtmp_dash_module.c b/hls/ngx_rtmp_dash_module.c index 31f6a44..210f456 100644 --- a/hls/ngx_rtmp_dash_module.c +++ b/hls/ngx_rtmp_dash_module.c @@ -1,3 +1,5 @@ + + #include #include #include @@ -5,21 +7,25 @@ #include "ngx_rtmp_live_module.h" #include "ngx_rtmp_mp4.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_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); + #define NGX_RTMP_DASH_BUFSIZE (1024*1024) #define NGX_RTMP_DASH_DIR_ACCESS 0744 #define NGX_RTMP_DASH_MAX_SIZE (800*1024) #define NGX_RTMP_DASH_MAX_SAMPLES 512 + typedef struct { ngx_uint_t video_earliest_pres_time; ngx_uint_t video_latest_pres_time; @@ -29,6 +35,7 @@ typedef struct { uint32_t id; } ngx_rtmp_dash_frag_t; + typedef struct { ngx_str_t playlist; ngx_str_t playlist_bak; @@ -60,11 +67,13 @@ typedef struct { ngx_str_t audio_fragment; } ngx_rtmp_dash_ctx_t; + typedef struct { ngx_str_t path; ngx_msec_t playlen; } ngx_rtmp_dash_cleanup_t; + typedef struct { ngx_flag_t dash; ngx_msec_t fraglen; @@ -75,6 +84,7 @@ typedef struct { ngx_path_t *slot; } ngx_rtmp_dash_app_conf_t; + static ngx_command_t ngx_rtmp_dash_commands[] = { { ngx_string("dash"), @@ -105,7 +115,6 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { 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, @@ -116,6 +125,7 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { ngx_null_command }; + static ngx_rtmp_module_t ngx_rtmp_dash_module_ctx = { NULL, /* preconfiguration */ ngx_rtmp_dash_postconfiguration, /* postconfiguration */ @@ -146,6 +156,7 @@ ngx_module_t ngx_rtmp_dash_module = { NGX_MODULE_V1_PADDING }; + static ngx_rtmp_dash_frag_t * ngx_rtmp_dash_get_frag(ngx_rtmp_session_t *s, ngx_int_t n) { @@ -158,6 +169,7 @@ ngx_rtmp_dash_get_frag(ngx_rtmp_session_t *s, ngx_int_t n) return &ctx->frags[(ctx->frag + n) % (hacf->winfrags * 2 + 1)]; } + static ngx_int_t ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) { @@ -170,6 +182,7 @@ ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) #endif } + static ngx_int_t ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { @@ -207,31 +220,46 @@ 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_HEADER \ + "\n" \ + "\n" \ " \n" -#define NGX_RTMP_DASH_MANIFEST_VIDEO \ - " \n"\ - " \n"\ - " \n"\ - " \n"\ +#define NGX_RTMP_DASH_MANIFEST_VIDEO \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" -#define NGX_RTMP_DASH_MANIFEST_AUDIO \ +#define NGX_RTMP_DASH_MANIFEST_AUDIO \ " \n"\ - " \n"\ - " \n"\ - " \n"\ - " \n"\ + " \n" \ + " \n" \ + " \n" \ + " \n" \ " \n" -#define NGX_RTMP_DASH_MANIFEST_FOOTER \ - " \n"\ +#define NGX_RTMP_DASH_MANIFEST_FOOTER \ + " \n" \ "\n" f = ngx_rtmp_dash_get_frag(s, hacf->winfrags/2); - audio_dur = f->id > 0 ? (uint32_t)(codec_ctx->sample_rate*((float)(f->video_latest_pres_time/f->id)/1000.0)) : - (uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)); + audio_dur = f->id > 0 ? (uint32_t)(codec_ctx->sample_rate * + ((float)(f->video_latest_pres_time/f->id)/1000.0)) : + (uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)); p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER, &ctx->start_time); @@ -239,28 +267,30 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->video) { p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_VIDEO, - codec_ctx->width, - codec_ctx->height, - codec_ctx->frame_rate, - codec_ctx->width, - codec_ctx->height, - codec_ctx->frame_rate, - (uint32_t)(live_ctx->stream->bw_in.bandwidth*8), - f->video_earliest_pres_time, - f->id > 0 ? (uint32_t)(f->video_latest_pres_time/f->id) : hacf->fraglen, - &ctx->name, - &ctx->name); + codec_ctx->width, + codec_ctx->height, + codec_ctx->frame_rate, + codec_ctx->width, + codec_ctx->height, + codec_ctx->frame_rate, + (uint32_t)(live_ctx->stream->bw_in.bandwidth*8), + f->video_earliest_pres_time, + f->id > 0 ? (uint32_t)(f->video_latest_pres_time / + f->id) : hacf->fraglen, + &ctx->name, + &ctx->name); n = ngx_write_fd(fd, buffer, p - buffer); } if (ctx->audio) { p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, - codec_ctx->sample_rate, - (uint32_t)(f->audio_earliest_pres_time*((float)codec_ctx->sample_rate/1000.0)), - codec_ctx->sample_rate, - audio_dur, - &ctx->name, - &ctx->name); + codec_ctx->sample_rate, + (uint32_t)(f->audio_earliest_pres_time * + ((float)codec_ctx->sample_rate/1000.0)), + codec_ctx->sample_rate, + audio_dur, + &ctx->name, + &ctx->name); n = ngx_write_fd(fd, buffer, p - buffer); } @@ -286,6 +316,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) return NGX_OK; } + static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) { @@ -308,7 +339,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) ngx_snprintf(path, sizeof(path), "%Vinit-video.dash",&ctx->stream); file.fd = ngx_open_file(path, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { @@ -351,7 +382,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) ngx_snprintf(path, sizeof(path), "%Vinit-audio.dash",&ctx->stream); file.fd = ngx_open_file(path, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { @@ -379,6 +410,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) return NGX_OK; } + static ngx_int_t ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) { @@ -416,12 +448,14 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata); pos = b->last; b->last += 44; /* leave room for sidx */ - ngx_rtmp_mp4_write_moof(b, f->video_earliest_pres_time, ctx->video_sample_count, - ctx->video_sample_sizes, (ctx->nfrags+ctx->frag),0); + ngx_rtmp_mp4_write_moof(b, f->video_earliest_pres_time, + ctx->video_sample_count, ctx->video_sample_sizes, + (ctx->nfrags+ctx->frag),0); pos1 = b->last; b->last = pos; - ngx_rtmp_mp4_write_sidx(s, b, ctx->video_mdat_size+8+(pos1-(pos+44)), f->video_earliest_pres_time, - f->video_latest_pres_time,0); + ngx_rtmp_mp4_write_sidx(s, b, ctx->video_mdat_size+8+(pos1-(pos+44)), + f->video_earliest_pres_time, + f->video_latest_pres_time,0); b->last = pos1; ngx_rtmp_mp4_write_mdat(b, ctx->video_mdat_size+8); @@ -433,7 +467,7 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) ngx_memzero(&file, sizeof(file)); file.log = s->connection->log; file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, @@ -448,11 +482,13 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) do { file_b.pos = file_b.last = file_b.start; if ((ssize_t)(written + sizeof(buffer)) > size) { - ngx_read_file(&ctx->video_file, file_b.start, size-written, ctx->video_file.offset); + ngx_read_file(&ctx->video_file, file_b.start, size-written, + ctx->video_file.offset); file_b.last += size-written; } else { - ngx_read_file(&ctx->video_file, file_b.start, sizeof(buffer), ctx->video_file.offset); + ngx_read_file(&ctx->video_file, file_b.start, sizeof(buffer), + ctx->video_file.offset); file_b.last += sizeof(buffer); } write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); @@ -467,7 +503,8 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->video_fragment.data); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%s'->'%s'",ctx->stream.data, ctx->video_fragment.data); + "dash: rename failed: '%s'->'%s'",ctx->stream.data, + ctx->video_fragment.data); return NGX_ERROR; } @@ -479,12 +516,14 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata); pos = b->last; b->last += 44; /* leave room for sidx */ - ngx_rtmp_mp4_write_moof(b, f->audio_earliest_pres_time, ctx->audio_sample_count, - ctx->audio_sample_sizes, (ctx->nfrags+ctx->frag), codec_ctx->sample_rate); + ngx_rtmp_mp4_write_moof(b, f->audio_earliest_pres_time, + ctx->audio_sample_count, ctx->audio_sample_sizes, + (ctx->nfrags+ctx->frag), codec_ctx->sample_rate); pos1 = b->last; b->last = pos; - ngx_rtmp_mp4_write_sidx(s, b, ctx->audio_mdat_size+8+(pos1-(pos+44)), f->audio_earliest_pres_time, - f->audio_latest_pres_time, codec_ctx->sample_rate); + ngx_rtmp_mp4_write_sidx(s, b, ctx->audio_mdat_size+8+(pos1-(pos+44)), + f->audio_earliest_pres_time, + f->audio_latest_pres_time, codec_ctx->sample_rate); b->last = pos1; ngx_rtmp_mp4_write_mdat(b, ctx->audio_mdat_size+8); @@ -496,7 +535,7 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) ngx_memzero(&file, sizeof(file)); file.log = s->connection->log; file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, @@ -511,11 +550,13 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) do { file_b.pos = file_b.last = file_b.start; if ((ssize_t)(written + sizeof(buffer)) > size) { - ngx_read_file(&ctx->audio_file, file_b.start, size-written, ctx->audio_file.offset); + ngx_read_file(&ctx->audio_file, file_b.start, size-written, + ctx->audio_file.offset); file_b.last += size-written; } else { - ngx_read_file(&ctx->audio_file, file_b.start, sizeof(buffer), ctx->audio_file.offset); + ngx_read_file(&ctx->audio_file, file_b.start, sizeof(buffer), + ctx->audio_file.offset); file_b.last += sizeof(buffer); } write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); @@ -530,13 +571,15 @@ ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->audio_fragment.data); if (rc != NGX_OK) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%s'->'%s'",ctx->stream.data, ctx->audio_fragment.data); + "dash: rename failed: '%s'->'%s'",ctx->stream.data, + ctx->audio_fragment.data); return NGX_ERROR; } return NGX_OK; } + static ngx_int_t ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) { @@ -585,7 +628,8 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) ctx->audio_file.log = s->connection->log; *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4v", f->id) = 0; - *ngx_sprintf(ctx->video_fragment.data + ctx->stream.len, "%uL.m4v", f->id) = 0; + *ngx_sprintf(ctx->video_fragment.data + ctx->stream.len, "%uL.m4v", + f->id) = 0; ngx_str_set(&ctx->video_file.name, "dash-v"); ctx->video_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, @@ -597,10 +641,12 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) } *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4a", f->id) = 0; - *ngx_sprintf(ctx->audio_fragment.data + ctx->stream.len, "%uL.m4a", f->id) = 0; + *ngx_sprintf(ctx->audio_fragment.data + ctx->stream.len, "%uL.m4a", + f->id) = 0; ngx_str_set(&ctx->audio_file.name, "dash-a"); ctx->audio_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); if (ctx->audio_file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, @@ -621,6 +667,7 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) return NGX_OK; } + static ngx_int_t ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { @@ -709,9 +756,11 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ctx->audio_fragment.len + NGX_INT64_LEN + sizeof(".m4a")); - ngx_memcpy(ctx->video_fragment.data, ctx->playlist.data, ctx->video_fragment.len - 1); + ngx_memcpy(ctx->video_fragment.data, ctx->playlist.data, + ctx->video_fragment.len - 1); ctx->video_fragment.data[ctx->video_fragment.len - 1] = '-'; - ngx_memcpy(ctx->audio_fragment.data, ctx->playlist.data, ctx->audio_fragment.len - 1); + ngx_memcpy(ctx->audio_fragment.data, ctx->playlist.data, + ctx->audio_fragment.len - 1); ctx->audio_fragment.data[ctx->audio_fragment.len - 1] = '-'; /* playlist path */ @@ -748,6 +797,7 @@ 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) { @@ -771,8 +821,10 @@ 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 ts) +ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, + uint32_t ts) { ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_dash_app_conf_t *hacf; @@ -785,8 +837,10 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, uint32 f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); - duration = ctx->video ? (f->video_latest_pres_time-f->video_earliest_pres_time) : - (f->audio_latest_pres_time-f->audio_earliest_pres_time); + duration = ctx->video ? (f->video_latest_pres_time - + f->video_earliest_pres_time) : + (f->audio_latest_pres_time - + f->audio_earliest_pres_time); if ((ctx->video) && ((int32_t)(hacf->fraglen - duration) > 150)) { boundary = 0; @@ -873,12 +927,14 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, bsize = out.end - out.last; } if (*in->buf->pos == 0xAF) { /* rtmp frame header */ - if (*(in->buf->pos+1) == 0x00) { /* rtmp audio frame number--skip 0 */ + /* rtmp audio frame number--skip 0 */ + if (*(in->buf->pos+1) == 0x00) { break; } else { if (bsize > 2) { - in->buf->pos += 2; /* skip two bytes of audio frame header */ + /* skip two bytes of audio frame header */ + in->buf->pos += 2; } } } @@ -901,8 +957,10 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ctx->audio_sample_count += 1; if ((ctx->audio_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES)) { - ctx->audio_sample_sizes[ctx->audio_sample_count] = ngx_rtmp_mp4_write_data(s, &ctx->audio_file, &out); - ctx->audio_mdat_size += ctx->audio_sample_sizes[ctx->audio_sample_count]; + ctx->audio_sample_sizes[ctx->audio_sample_count] = + ngx_rtmp_mp4_write_data(s, &ctx->audio_file, &out); + ctx->audio_mdat_size += + ctx->audio_sample_sizes[ctx->audio_sample_count]; } else { ctx->audio_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; @@ -912,6 +970,7 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } + static ngx_int_t ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) @@ -1000,8 +1059,10 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ctx->video_sample_count += 1; if (ctx->video_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES) { - ctx->video_sample_sizes[ctx->video_sample_count] = ngx_rtmp_mp4_write_data(s, &ctx->video_file, &out); - ctx->video_mdat_size += ctx->video_sample_sizes[ctx->video_sample_count]; + ctx->video_sample_sizes[ctx->video_sample_count] = + ngx_rtmp_mp4_write_data(s, &ctx->video_file, &out); + ctx->video_mdat_size += + ctx->video_sample_sizes[ctx->video_sample_count]; } else { ctx->video_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; @@ -1010,6 +1071,7 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } + static void ngx_rtmp_dash_discontinue(ngx_rtmp_session_t *s) { @@ -1028,6 +1090,7 @@ ngx_rtmp_dash_discontinue(ngx_rtmp_session_t *s) } } + static ngx_int_t ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) { @@ -1045,6 +1108,7 @@ ngx_rtmp_dash_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v) return next_stream_eof(s, v); } + static ngx_int_t ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) { @@ -1180,6 +1244,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) } } + static time_t ngx_rtmp_dash_cleanup(void *data) { @@ -1190,6 +1255,7 @@ ngx_rtmp_dash_cleanup(void *data) return cleanup->playlen / 500; } + static void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) { @@ -1288,4 +1354,4 @@ ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) ngx_rtmp_stream_eof = ngx_rtmp_dash_stream_eof; return NGX_OK; -} \ No newline at end of file +} diff --git a/hls/ngx_rtmp_mp4.c b/hls/ngx_rtmp_mp4.c index 076e99c..e2a8c25 100644 --- a/hls/ngx_rtmp_mp4.c +++ b/hls/ngx_rtmp_mp4.c @@ -1,13 +1,16 @@ + #include #include #include "ngx_rtmp_mp4.h" #include + static u_char compressor_name[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + ngx_int_t ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) { @@ -27,6 +30,7 @@ ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) { @@ -42,6 +46,7 @@ ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) { @@ -56,6 +61,7 @@ ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) { @@ -69,6 +75,7 @@ ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) { @@ -81,11 +88,12 @@ ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { //int i = 3; - /* initially stolen from ffmpeg, but most of it doesnt appear to be necessary */ + /* initially stolen from ffmpeg, but most of it isnt necessary */ ngx_rtmp_mp4_field_8(b, tag); //for (; i > 0; i--) { @@ -96,6 +104,7 @@ ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) { @@ -111,13 +120,14 @@ ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) return NGX_OK; } + /* transformation matrix |a b u| |c d v| |tx ty w| */ ngx_int_t ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, - int16_t d, int16_t tx, int16_t ty) + int16_t d, int16_t tx, int16_t ty) { ngx_rtmp_mp4_field_32(buf, a << 16); /* 16.16 format */ ngx_rtmp_mp4_field_32(buf, b << 16); /* 16.16 format */ @@ -132,8 +142,10 @@ ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -147,7 +159,8 @@ ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t metadata ngx_rtmp_mp4_field_32(b, 1); if (metadata.video == 1) { - b->last = ngx_cpymem(b->last, "avc1iso5dash", sizeof("avc1iso5dash")-1); + b->last = ngx_cpymem(b->last, "avc1iso5dash", + sizeof("avc1iso5dash")-1); } else { b->last = ngx_cpymem(b->last, "iso5dash", sizeof("iso5dash")-1); @@ -163,10 +176,11 @@ ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t metadata } ngx_rtmp_mp4_update_box_size(b, pos); - + return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) { @@ -184,8 +198,8 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* modification time */ ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); /* timescale */ ngx_rtmp_mp4_field_32(b, 0); /* duration */ - ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_PREFERRED_RATE); /* preferred playback rate */ - ngx_rtmp_mp4_field_16(b, NGX_RTMP_MP4_PREFERRED_VOLUME); /* preferred volume rate */ + ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_PREFERRED_RATE); /* playback rate */ + ngx_rtmp_mp4_field_16(b, NGX_RTMP_MP4_PREFERRED_VOLUME); /* volume rate */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ @@ -206,6 +220,7 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) { @@ -227,7 +242,8 @@ ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* duration */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0x00000001 : 0); /* 2 16s, layer and alternate group */ + /* 2 16s, layer and alternate group */ + ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0x00000001 : 0); ngx_rtmp_mp4_field_16(b, metadata.audio == 1 ? 0x0100 : 0); ngx_rtmp_mp4_field_16(b, 0); /* reserved */ @@ -247,6 +263,7 @@ ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) { @@ -262,7 +279,9 @@ ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 0); /* creation time */ ngx_rtmp_mp4_field_32(b, 0); /* modification time */ - ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? metadata.sample_rate : NGX_RTMP_MP4_TIMESCALE); /* time scale*/ + /* time scale*/ + ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? metadata.sample_rate : + NGX_RTMP_MP4_TIMESCALE); ngx_rtmp_mp4_field_32(b, 0); /* duration */ ngx_rtmp_mp4_field_16(b, 0x15C7); /* language */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ @@ -272,6 +291,7 @@ ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) { @@ -287,10 +307,12 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* version and flags */ ngx_rtmp_mp4_field_32(b, 0); /* pre defined (=0) */ if (metadata.video == 1) { - b->last = ngx_cpymem(b->last, "vide", sizeof("vide")-1); /* video handler */ + /* video handler */ + b->last = ngx_cpymem(b->last, "vide", sizeof("vide")-1); } else { - b->last = ngx_cpymem(b->last, "soun", sizeof("soun")-1); /* sound handler */ + /* sound handler */ + b->last = ngx_cpymem(b->last, "soun", sizeof("soun")-1); } ngx_rtmp_mp4_field_32(b, 0); /* reserved */ @@ -298,10 +320,12 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* reserved */ if (metadata.video == 1) { - b->last = ngx_cpymem(b->last, "VideoHandler", sizeof("VideoHandler")); /* video handler string--NULL TERMINATED */ + /* video handler string--NULL TERMINATED */ + b->last = ngx_cpymem(b->last, "VideoHandler", sizeof("VideoHandler")); } else { - b->last = ngx_cpymem(b->last, "SoundHandler", sizeof("SoundHandler")); /* sound handler string--NULL TERMINATED */ + /* sound handler string--NULL TERMINATED */ + b->last = ngx_cpymem(b->last, "SoundHandler", sizeof("SoundHandler")); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -309,6 +333,7 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_vmhd(ngx_buf_t *b) { @@ -324,6 +349,7 @@ ngx_rtmp_mp4_write_vmhd(ngx_buf_t *b) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) { @@ -339,6 +365,7 @@ ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) { @@ -364,6 +391,7 @@ ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) { @@ -383,8 +411,10 @@ ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos, *p; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -427,8 +457,10 @@ ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metada return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -473,10 +505,10 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metad return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) { - /* SCREW THIS FUNCTION */ u_char *pos; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -507,14 +539,17 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) b->last = ngx_cpymem(b->last, "esds", sizeof("esds")-1); ngx_rtmp_mp4_field_32(b, 0); /* version */ - ngx_rtmp_mp4_put_descr(b, 0x03, 23+decoder_info); /* length of the rest of the box */ + /* length of the rest of the box */ + ngx_rtmp_mp4_put_descr(b, 0x03, 23+decoder_info); ngx_rtmp_mp4_field_16(b, 1); /* track id */ ngx_rtmp_mp4_field_8(b, 0x00); /* flags */ - ngx_rtmp_mp4_put_descr(b, 0x04, 15+decoder_info); /* length of the rest of the box */ + /* length of the rest of the box */ + ngx_rtmp_mp4_put_descr(b, 0x04, 15+decoder_info); ngx_rtmp_mp4_field_8(b, 0x40); /* codec id */ ngx_rtmp_mp4_field_8(b, 0x15); /* audio stream */ ngx_rtmp_mp4_field_24(b, 0); /* buffersize? */ - ngx_rtmp_mp4_field_32(b, 0x0001F151); /* bitrate TODO: should probably set it dynamically*/ + /* bitrate TODO: should probably set it dynamically*/ + ngx_rtmp_mp4_field_32(b, 0x0001F151); ngx_rtmp_mp4_field_32(b, 0x0001F14D); /* I really dont know */ if (aac) { @@ -530,8 +565,10 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -564,8 +601,10 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metad return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -591,6 +630,7 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metada return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_stts(ngx_buf_t *b) { @@ -611,6 +651,7 @@ ngx_rtmp_mp4_write_stts(ngx_buf_t *b) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) { @@ -631,6 +672,7 @@ ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) { @@ -652,6 +694,7 @@ ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_stco(ngx_buf_t *b) { @@ -672,8 +715,10 @@ ngx_rtmp_mp4_write_stco(ngx_buf_t *b) return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -695,8 +740,10 @@ ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metada return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -722,8 +769,10 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metada return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -744,7 +793,8 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metada } ngx_int_t -ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -763,6 +813,7 @@ ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metada return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) { @@ -770,7 +821,8 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) uint32_t sample_dur; if (metadata.video == 1) { - sample_dur = metadata.frame_rate > 0 ? NGX_RTMP_MP4_TIMESCALE/metadata.frame_rate : NGX_RTMP_MP4_TIMESCALE; + sample_dur = metadata.frame_rate > 0 ? NGX_RTMP_MP4_TIMESCALE / + metadata.frame_rate : NGX_RTMP_MP4_TIMESCALE; } else { sample_dur = 1024; @@ -801,15 +853,18 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 1); /* default sample description index */ ngx_rtmp_mp4_field_32(b, sample_dur); /* default sample duration */ ngx_rtmp_mp4_field_32(b, 0); /* default sample size */ - ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0 : 0x00010000); /* default sample flags */ + /* default sample flags */ + ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0 : 0x00010000); ngx_rtmp_mp4_update_box_size(b, pos); return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -829,6 +884,7 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metada return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) { @@ -841,7 +897,8 @@ ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) b->last = ngx_cpymem(b->last, "tfhd", sizeof("tfhd")-1); - ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00020020 : 0x00020000); /* version & flags */ + /* version & flags */ + ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00020020 : 0x00020000); ngx_rtmp_mp4_field_32(b, 1); /* track id */ if (sample_rate > 0) { ngx_rtmp_mp4_field_32(b, 0x02000000); @@ -852,8 +909,10 @@ ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, + ngx_uint_t sample_rate) { u_char *pos; float multiplier; @@ -873,16 +932,19 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, ngx_uint_t b->last = ngx_cpymem(b->last, "tfdt", sizeof("tfdt")-1); ngx_rtmp_mp4_field_32(b, 0x00000000); /* version == 1 aka 64 bit integer */ - ngx_rtmp_mp4_field_32(b, (uint32_t)((float)earliest_pres_time*multiplier)); /* no idea */ + /* earliest presentation time */ + ngx_rtmp_mp4_field_32(b, (uint32_t)((float)earliest_pres_time*multiplier)); ngx_rtmp_mp4_update_box_size(b, pos); return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, uint32_t sample_sizes[128], - u_char *moof_pos, ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, + uint32_t sample_sizes[128], u_char *moof_pos, + ngx_uint_t sample_rate) { u_char *pos; uint32_t i, offset; @@ -890,10 +952,12 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, uint32_t sample_siz pos = b->last; if (sample_rate > 0) { - offset = (pos-moof_pos) + 20 + (sample_count*4) + 8; /* moof stuff + trun stuff + sample entries + mdat header */ + /* moof stuff + trun stuff + sample entries + mdat header */ + offset = (pos-moof_pos) + 20 + (sample_count*4) + 8; } else { - offset = (pos-moof_pos) + 24 + (sample_count*4) + 8; /* moof stuff + trun stuff + sample entries + mdat header */ + /* moof stuff + trun stuff + sample entries + mdat header */ + offset = (pos-moof_pos) + 24 + (sample_count*4) + 8; } @@ -902,7 +966,8 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, uint32_t sample_siz b->last = ngx_cpymem(b->last, "trun", sizeof("trun")-1); - ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00000201 : 0x00000205); /* version and flags */ + /* version and flags */ + ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00000201 : 0x00000205); ngx_rtmp_mp4_field_32(b, sample_count); /* sample count */ ngx_rtmp_mp4_field_32(b, offset); /* data offset */ if (sample_rate == 0) { @@ -918,9 +983,11 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, uint32_t sample_siz return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sample_count, uint32_t sample_sizes[128], - u_char *moof_pos, ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, + uint32_t sample_count, uint32_t sample_sizes[128], + u_char *moof_pos, ngx_uint_t sample_rate) { u_char *pos; @@ -933,13 +1000,15 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sa ngx_rtmp_mp4_write_tfhd(b, sample_rate); ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time, sample_rate); - ngx_rtmp_mp4_write_trun(b, sample_count, sample_sizes, moof_pos, sample_rate); + ngx_rtmp_mp4_write_trun(b, sample_count, sample_sizes, moof_pos, + sample_rate); ngx_rtmp_mp4_update_box_size(b, pos); return NGX_OK; } + ngx_int_t ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) { @@ -960,16 +1029,21 @@ ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t reference_size, - ngx_uint_t earliest_pres_time, ngx_uint_t latest_pres_time, ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_uint_t reference_size, + ngx_uint_t earliest_pres_time, + ngx_uint_t latest_pres_time, ngx_uint_t sample_rate) { u_char *pos; uint32_t ept, dur; if (sample_rate > 0) { - ept = (uint32_t)((float)earliest_pres_time*((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); - dur = (uint32_t)((float)(latest_pres_time-earliest_pres_time)*((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); + ept = (uint32_t)((float)earliest_pres_time*((float)sample_rate / + (float)NGX_RTMP_MP4_TIMESCALE)); + dur = (uint32_t)((float)(latest_pres_time-earliest_pres_time) * + ((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); } else { ept = earliest_pres_time; @@ -988,14 +1062,18 @@ ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t referenc ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 1); /* reference id */ - ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? sample_rate : NGX_RTMP_MP4_TIMESCALE); /* timescale */ + /* timescale */ + ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? sample_rate : + NGX_RTMP_MP4_TIMESCALE); ngx_rtmp_mp4_field_32(b, ept); /* earliest presentation time */ ngx_rtmp_mp4_field_32(b, 0); /* first offset */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ ngx_rtmp_mp4_field_16(b, 1); /* reference count (=1) */ - ngx_rtmp_mp4_field_32(b, reference_size); /* 1st bit is reference type, the rest is reference size */ + /* 1st bit is reference type, the rest is reference size */ + ngx_rtmp_mp4_field_32(b, reference_size); ngx_rtmp_mp4_field_32(b, dur); /* subsegment duration */ - ngx_rtmp_mp4_field_8(b, 0x90); /* first bit is startsWithSAP (=1), next 3 bits are SAP type (=001) */ + /* first bit is startsWithSAP (=1), next 3 bits are SAP type (=001) */ + ngx_rtmp_mp4_field_8(b, 0x90); ngx_rtmp_mp4_field_24(b, 0); /* SAP delta time */ ngx_rtmp_mp4_update_box_size(b, pos); @@ -1003,9 +1081,11 @@ ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t referenc return NGX_OK; } + ngx_int_t -ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sample_count, - uint32_t sample_sizes[128], uint32_t index, ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, + uint32_t sample_count, uint32_t sample_sizes[128], + uint32_t index, ngx_uint_t sample_rate) { u_char *pos; @@ -1017,13 +1097,15 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sa b->last = ngx_cpymem(b->last, "moof", sizeof("moof")-1); ngx_rtmp_mp4_write_mfhd(b, index); - ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, sample_sizes, pos, sample_rate); + ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, sample_sizes, + pos, sample_rate); ngx_rtmp_mp4_update_box_size(b, pos); return NGX_OK; } + ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) { @@ -1034,6 +1116,7 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) return NGX_OK; } + uint32_t ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, ngx_buf_t *b) { @@ -1061,4 +1144,4 @@ ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, ngx_buf_t *b) } return size; -} \ No newline at end of file +} diff --git a/hls/ngx_rtmp_mp4.h b/hls/ngx_rtmp_mp4.h index 0cd2b51..bfef725 100644 --- a/hls/ngx_rtmp_mp4.h +++ b/hls/ngx_rtmp_mp4.h @@ -1,4 +1,5 @@ + #ifndef _NGX_RTMP_MP4_H_INCLUDED_ #define _NGX_RTMP_MP4_H_INCLUDED_ @@ -7,6 +8,7 @@ #include #include + typedef struct { ngx_uint_t width; ngx_uint_t height; @@ -16,23 +18,38 @@ typedef struct { ngx_uint_t frame_rate; } ngx_rtmp_mp4_metadata_t; + enum { NGX_RTMP_MP4_FILETYPE_INIT = 0, NGX_RTMP_MP4_FILETYPE_SEG = 1 }; -#define NGX_RTMP_MP4_TIMESCALE 1000 /* divide all times by this value. this is the same resolution as RTMP so it is convenient */ -#define NGX_RTMP_MP4_PREFERRED_RATE 0x00010000 /* normal forward playback as defined by spec */ -#define NGX_RTMP_MP4_PREFERRED_VOLUME 0x0100 /* full volume as defined by spec */ -ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t metadata); -ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata); -ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sample_count, - uint32_t sample_sizes[128], uint32_t index, ngx_uint_t sample_rate); -ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t reference_size, - ngx_uint_t earliest_pres_time, ngx_uint_t latest_pres_time, ngx_uint_t sample_rate); +/* divide all times by this value. this is the same resolution as RTMP so it +is convenient */ +#define NGX_RTMP_MP4_TIMESCALE 1000 +/* normal forward playback as defined by spec */ +#define NGX_RTMP_MP4_PREFERRED_RATE 0x00010000 +/* full volume as defined by spec */ +#define NGX_RTMP_MP4_PREFERRED_VOLUME 0x0100 + +ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, + ngx_rtmp_mp4_metadata_t metadata); +ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata); +ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, + ngx_uint_t earliest_pres_time, + uint32_t sample_count, + uint32_t sample_sizes[128], uint32_t index, + ngx_uint_t sample_rate); +ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_uint_t reference_size, + ngx_uint_t earliest_pres_time, + ngx_uint_t latest_pres_time, + ngx_uint_t sample_rate); ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); -uint32_t ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, ngx_buf_t *b); +uint32_t ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, + ngx_buf_t *b); #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ \ No newline at end of file From 9177be55dd46cd3c2831e612c60ee3ffdc0065be Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Wed, 9 Oct 2013 16:12:47 -0400 Subject: [PATCH 07/50] MPD tweaks and stubbed out MP3 support --- hls/ngx_rtmp_dash_module.c | 42 ++++++++++++++++++++++++++------------ hls/ngx_rtmp_mp4.c | 20 ++++++++++-------- hls/ngx_rtmp_mp4.h | 1 + 3 files changed, 41 insertions(+), 22 deletions(-) diff --git a/hls/ngx_rtmp_dash_module.c b/hls/ngx_rtmp_dash_module.c index 210f456..386ec42 100644 --- a/hls/ngx_rtmp_dash_module.c +++ b/hls/ngx_rtmp_dash_module.c @@ -223,10 +223,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) #define NGX_RTMP_DASH_MANIFEST_HEADER \ "\n" \ "\n" \ - " \n" + "availabilityStartTime=\"%V\" minBufferTime=\"PT3S\" " \ + "timeShiftBufferDepth=\"PT0H0M0.00S\" " \ + "profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n " \ + " \n" #define NGX_RTMP_DASH_MANIFEST_VIDEO \ " \n" \ @@ -244,7 +244,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) "schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" " \ "value=\"1\"/>\n" \ " \n" \ " sample_rate*(hacf->fraglen/1000)); p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER, - &ctx->start_time); + &ctx->start_time); n = ngx_write_fd(fd, buffer, p - buffer); if (ctx->video) { @@ -273,9 +273,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, - (uint32_t)(live_ctx->stream->bw_in.bandwidth*8), + (ngx_uint_t)(live_ctx->stream->bw_in.bandwidth*8), f->video_earliest_pres_time, - f->id > 0 ? (uint32_t)(f->video_latest_pres_time / + f->id > 0 ? (ngx_uint_t)(f->video_latest_pres_time / f->id) : hacf->fraglen, &ctx->name, &ctx->name); @@ -284,8 +284,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->audio) { p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, + codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? + "40.2" : "6b", codec_ctx->sample_rate, - (uint32_t)(f->audio_earliest_pres_time * + (ngx_uint_t)(f->audio_earliest_pres_time * ((float)codec_ctx->sample_rate/1000.0)), codec_ctx->sample_rate, audio_dur, @@ -368,6 +370,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) metadata.video = 1; metadata.sample_rate = codec_ctx->sample_rate; metadata.frame_rate = codec_ctx->frame_rate; + metadata.audio_codec = codec_ctx->audio_codec_id; ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_INIT, metadata); ngx_rtmp_mp4_write_moov(s, b, metadata); @@ -901,7 +904,15 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } - if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC || + /* Disable MP3 for now + if ((codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC) && + (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_MP3)) */ + if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC) + { + return NGX_OK; + } + + if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC && codec_ctx->aac_header == NULL) { return NGX_OK; @@ -918,6 +929,10 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ctx->audio = 1; + if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_MP3) { + in->buf->pos += 1; + } + for (; in && out.last < out.end; in = in->next) { bsize = in->buf->last - in->buf->pos; if (bsize < 4) { @@ -926,19 +941,20 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, if (out.last + bsize > out.end) { bsize = out.end - out.last; } - if (*in->buf->pos == 0xAF) { /* rtmp frame header */ + if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC && *in->buf->pos == 0xAF) { /* rtmp frame header */ /* rtmp audio frame number--skip 0 */ if (*(in->buf->pos+1) == 0x00) { break; } else { - if (bsize > 2) { + if (bsize > 3) { /* skip two bytes of audio frame header */ in->buf->pos += 2; + /* skip the extra mp3 byte */ } } } - if (!in->next) { + if (!in->next && codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) { bsize -= 2; /* chop 2 bytes off the end of the frame */ } out.last = ngx_cpymem(out.last, in->buf->pos, bsize); diff --git a/hls/ngx_rtmp_mp4.c b/hls/ngx_rtmp_mp4.c index e2a8c25..9c8ac8a 100644 --- a/hls/ngx_rtmp_mp4.c +++ b/hls/ngx_rtmp_mp4.c @@ -507,7 +507,8 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_int_t -ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) +ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t metadata) { u_char *pos; @@ -528,7 +529,7 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) aac_header_offset = 0; } else { - decoder_info = (aac->buf->last-aac->buf->pos)-2; + decoder_info = (aac->buf->last-aac->buf->pos); aac_header_offset = 2; } pos = b->last; @@ -540,17 +541,18 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b) ngx_rtmp_mp4_field_32(b, 0); /* version */ /* length of the rest of the box */ - ngx_rtmp_mp4_put_descr(b, 0x03, 23+decoder_info); + ngx_rtmp_mp4_put_descr(b, 0x03, 21+decoder_info); ngx_rtmp_mp4_field_16(b, 1); /* track id */ ngx_rtmp_mp4_field_8(b, 0x00); /* flags */ /* length of the rest of the box */ - ngx_rtmp_mp4_put_descr(b, 0x04, 15+decoder_info); - ngx_rtmp_mp4_field_8(b, 0x40); /* codec id */ + ngx_rtmp_mp4_put_descr(b, 0x04, 13+decoder_info); + ngx_rtmp_mp4_field_8(b, metadata.audio_codec == NGX_RTMP_AUDIO_AAC ? 0x40 : + 0x6B); /* codec id */ ngx_rtmp_mp4_field_8(b, 0x15); /* audio stream */ ngx_rtmp_mp4_field_24(b, 0); /* buffersize? */ - /* bitrate TODO: should probably set it dynamically*/ + /* Next two fields are bitrate. */ ngx_rtmp_mp4_field_32(b, 0x0001F151); - ngx_rtmp_mp4_field_32(b, 0x0001F14D); /* I really dont know */ + ngx_rtmp_mp4_field_32(b, 0x0001F14D); if (aac) { ngx_rtmp_mp4_put_descr(b, 0x05, decoder_info); @@ -592,7 +594,7 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_16(b, metadata.sample_rate); /* sample rate */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ - ngx_rtmp_mp4_write_esds(s, b); + ngx_rtmp_mp4_write_esds(s, b, metadata); ngx_rtmp_mp4_field_32(b, 8); /* size */ ngx_rtmp_mp4_field_32(b, 0); /* null tag */ @@ -825,7 +827,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) metadata.frame_rate : NGX_RTMP_MP4_TIMESCALE; } else { - sample_dur = 1024; + sample_dur = metadata.audio_codec == NGX_RTMP_AUDIO_AAC ? 1024 : 1152; } pos = b->last; diff --git a/hls/ngx_rtmp_mp4.h b/hls/ngx_rtmp_mp4.h index bfef725..3226232 100644 --- a/hls/ngx_rtmp_mp4.h +++ b/hls/ngx_rtmp_mp4.h @@ -16,6 +16,7 @@ typedef struct { ngx_uint_t video; ngx_uint_t sample_rate; ngx_uint_t frame_rate; + ngx_uint_t audio_codec; } ngx_rtmp_mp4_metadata_t; From 040c96ed9bf94e69c66ba6d3614d80d66428da30 Mon Sep 17 00:00:00 2001 From: Stephen Basile Date: Sun, 20 Oct 2013 14:36:28 -0400 Subject: [PATCH 08/50] moved dash to its own folder --- config | 6 +++--- {hls => dash}/ngx_rtmp_dash_module.c | 0 {hls => dash}/ngx_rtmp_mp4.c | 0 {hls => dash}/ngx_rtmp_mp4.h | 0 4 files changed, 3 insertions(+), 3 deletions(-) rename {hls => dash}/ngx_rtmp_dash_module.c (100%) rename {hls => dash}/ngx_rtmp_mp4.c (100%) rename {hls => dash}/ngx_rtmp_mp4.h (100%) diff --git a/config b/config index e6455b2..16bbf57 100644 --- a/config +++ b/config @@ -44,7 +44,7 @@ NGX_ADDON_DEPS="$NGX_ADDON_DEPS \ $ngx_addon_dir/ngx_rtmp_relay_module.h \ $ngx_addon_dir/ngx_rtmp_streams.h \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.h \ - $ngx_addon_dir/hls/ngx_rtmp_mp4.h \ + $ngx_addon_dir/dash/ngx_rtmp_mp4.h \ " @@ -79,9 +79,9 @@ NGX_ADDON_SRCS="$NGX_ADDON_SRCS \ $ngx_addon_dir/ngx_rtmp_log_module.c \ $ngx_addon_dir/ngx_rtmp_limit_module.c \ $ngx_addon_dir/hls/ngx_rtmp_hls_module.c \ - $ngx_addon_dir/hls/ngx_rtmp_dash_module.c \ + $ngx_addon_dir/dash/ngx_rtmp_dash_module.c \ $ngx_addon_dir/hls/ngx_rtmp_mpegts.c \ - $ngx_addon_dir/hls/ngx_rtmp_mp4.c \ + $ngx_addon_dir/dash/ngx_rtmp_mp4.c \ " CFLAGS="$CFLAGS -I$ngx_addon_dir" diff --git a/hls/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c similarity index 100% rename from hls/ngx_rtmp_dash_module.c rename to dash/ngx_rtmp_dash_module.c diff --git a/hls/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c similarity index 100% rename from hls/ngx_rtmp_mp4.c rename to dash/ngx_rtmp_mp4.c diff --git a/hls/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h similarity index 100% rename from hls/ngx_rtmp_mp4.h rename to dash/ngx_rtmp_mp4.h From 7ea7690db2a6b6e72384889558004c3f3f293b8b Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Sun, 10 Nov 2013 23:24:31 +0400 Subject: [PATCH 09/50] refactored dash & fixed style --- dash/ngx_rtmp_dash_module.c | 1112 +++++++++++++++-------------------- dash/ngx_rtmp_mp4.c | 265 +++++---- dash/ngx_rtmp_mp4.h | 36 +- 3 files changed, 623 insertions(+), 790 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 386ec42..107b4f6 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -20,20 +20,22 @@ static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child); -#define NGX_RTMP_DASH_BUFSIZE (1024*1024) -#define NGX_RTMP_DASH_DIR_ACCESS 0744 -#define NGX_RTMP_DASH_MAX_SIZE (800*1024) -#define NGX_RTMP_DASH_MAX_SAMPLES 512 +#define NGX_RTMP_DASH_BUFSIZE (1024*1024) +#define NGX_RTMP_DASH_MAX_MDAT (800*1024) +#define NGX_RTMP_DASH_MAX_SAMPLES 512 typedef struct { - ngx_uint_t video_earliest_pres_time; - ngx_uint_t video_latest_pres_time; - ngx_uint_t audio_earliest_pres_time; - ngx_uint_t audio_latest_pres_time; - unsigned SAP:1; - uint32_t id; -} ngx_rtmp_dash_frag_t; + ngx_uint_t id; + ngx_uint_t opened; + ngx_uint_t mdat_size; + ngx_uint_t sample_count; + ngx_file_t file; + char type; + uint32_t earliest_pres_time; + uint32_t latest_pres_time; + uint32_t sample_sizes[NGX_RTMP_DASH_MAX_SAMPLES]; +} ngx_rtmp_dash_track_t; typedef struct { @@ -44,27 +46,17 @@ typedef struct { ngx_str_t start_time; unsigned opened:1; - unsigned video:1; - unsigned audio:1; + unsigned has_video:1; + unsigned has_audio:1; ngx_file_t video_file; ngx_file_t audio_file; - uint32_t frag; - uint32_t nfrags; - ngx_rtmp_dash_frag_t *frags; + ngx_uint_t id; + uint32_t fragment_end; - ngx_buf_t *buffer; - - ngx_uint_t video_mdat_size; - uint32_t video_sample_count; - uint32_t video_sample_sizes[NGX_RTMP_DASH_MAX_SAMPLES]; - ngx_str_t video_fragment; - - ngx_uint_t audio_mdat_size; - uint32_t audio_sample_count; - uint32_t audio_sample_sizes[NGX_RTMP_DASH_MAX_SAMPLES]; - ngx_str_t audio_fragment; + ngx_rtmp_dash_track_t audio; + ngx_rtmp_dash_track_t video; } ngx_rtmp_dash_ctx_t; @@ -127,49 +119,36 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { static ngx_rtmp_module_t ngx_rtmp_dash_module_ctx = { - NULL, /* preconfiguration */ - ngx_rtmp_dash_postconfiguration, /* postconfiguration */ + NULL, /* preconfiguration */ + ngx_rtmp_dash_postconfiguration, /* postconfiguration */ - NULL, /* create main configuration */ - NULL, /* init main configuration */ + NULL, /* create main configuration */ + NULL, /* init main configuration */ - NULL, /* create server configuration */ - NULL, /* merge server 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_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 + 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 *hacf; - - hacf = 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) % (hacf->winfrags * 2 + 1)]; -} - - static ngx_int_t ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) { @@ -186,86 +165,111 @@ ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) static ngx_int_t ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { - static u_char buffer[2048]; - int fd; - u_char *p; - ngx_rtmp_dash_app_conf_t *hacf; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_rtmp_live_ctx_t *live_ctx; - ssize_t n; - ngx_str_t playlist, playlist_bak; - ngx_rtmp_dash_frag_t *f; - uint32_t audio_dur; + u_char *p; + ssize_t n; + ngx_fd_t fd; + ngx_str_t playlist, playlist_bak; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_live_ctx_t *live_ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_app_conf_t *dacf; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + 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); live_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); - if (hacf == NULL || ctx == NULL || codec_ctx == NULL || - live_ctx == NULL || live_ctx->stream == NULL) { + if (dacf == NULL || ctx == NULL || codec_ctx == NULL || + live_ctx == NULL || live_ctx->stream == NULL) + { return NGX_ERROR; } - /* done playlists */ - 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 \ - "\n" \ - "\n " \ - " \n" -#define NGX_RTMP_DASH_MANIFEST_VIDEO \ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" -#define NGX_RTMP_DASH_MANIFEST_AUDIO \ - " \n"\ - " \n" \ - " \n" \ - " \n" \ - " \n" \ - " \n" -#define NGX_RTMP_DASH_MANIFEST_FOOTER \ - " \n" \ + +#define NGX_RTMP_DASH_MANIFEST_HEADER \ + "\n" \ + "\n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_VIDEO \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_AUDIO \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_FOOTER \ + " \n" \ "\n" - f = ngx_rtmp_dash_get_frag(s, hacf->winfrags/2); - - audio_dur = f->id > 0 ? (uint32_t)(codec_ctx->sample_rate * - ((float)(f->video_latest_pres_time/f->id)/1000.0)) : - (uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)); - p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER, &ctx->start_time); n = ngx_write_fd(fd, buffer, p - buffer); - if (ctx->video) { + if (ctx->has_video) { p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_VIDEO, codec_ctx->width, codec_ctx->height, @@ -273,24 +277,24 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, - (ngx_uint_t)(live_ctx->stream->bw_in.bandwidth*8), - f->video_earliest_pres_time, - f->id > 0 ? (ngx_uint_t)(f->video_latest_pres_time / - f->id) : hacf->fraglen, - &ctx->name, + (ngx_uint_t) (live_ctx->stream->bw_in.bandwidth * 8), + ctx->video.earliest_pres_time, + (ngx_uint_t) dacf->fraglen, + &ctx->name, &ctx->name); n = ngx_write_fd(fd, buffer, p - buffer); } - if (ctx->audio) { + if (ctx->has_audio) { p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? - "40.2" : "6b", - codec_ctx->sample_rate, - (ngx_uint_t)(f->audio_earliest_pres_time * - ((float)codec_ctx->sample_rate/1000.0)), + "40.2" : "6b", codec_ctx->sample_rate, - audio_dur, + (ngx_uint_t) (ctx->audio.earliest_pres_time * + (float) codec_ctx->sample_rate / 1000), + codec_ctx->sample_rate, + (ngx_uint_t) (dacf->fraglen * codec_ctx->sample_rate + / 1000), &ctx->name, &ctx->name); n = ngx_write_fd(fd, buffer, p - buffer); @@ -308,7 +312,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_close_file(fd); - if (ngx_rename_file(ctx->playlist_bak.data, ctx->playlist.data)) { + if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data)) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: rename failed: '%V'->'%V'", &playlist_bak, &playlist); @@ -322,348 +326,287 @@ 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_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - int rc; - ngx_file_t file; - static u_char path[1024]; - ngx_buf_t *b; - ngx_rtmp_mp4_metadata_t metadata; + ngx_int_t rc; + ngx_buf_t b; + ngx_file_t file; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_mp4_metadata_t metadata; + + 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 || !codec_ctx) { + + if (ctx == NULL || codec_ctx == NULL) { return NGX_ERROR; } - ngx_memzero(&path, sizeof(path)); - ngx_str_set(&file.name, "dash-init-video"); - ngx_snprintf(path, sizeof(path), "%Vinit-video.dash",&ctx->stream); + ngx_memzero(&file, sizeof(ngx_file_t)); + file.log = s->connection->log; - file.fd = ngx_open_file(path, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + metadata.width = codec_ctx->width; + metadata.height = codec_ctx->height; + metadata.sample_rate = codec_ctx->sample_rate; + metadata.frame_rate = codec_ctx->frame_rate; + metadata.audio_codec = codec_ctx->audio_codec_id; + + /* init video */ + + ngx_str_set(&file.name, "dash-init-video"); + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4v") = 0; + + file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: error creating video init file"); return NGX_ERROR; } - file.log = s->connection->log; + b.start = buffer; + b.end = b.start + sizeof(buffer); + b.pos = b.last = b.start; - b = ngx_pcalloc(s->connection->pool, sizeof(ngx_buf_t)); - if (b == NULL) { - return NGX_ERROR; - } - b->start = ngx_palloc(s->connection->pool, 1024); - if (b->start == NULL) { - return NGX_ERROR; - } - - b->end = b->start + 1024; - b->pos = b->last = b->start; - - metadata.width = codec_ctx->width; - metadata.height = codec_ctx->height; metadata.audio = 0; metadata.video = 1; - metadata.sample_rate = codec_ctx->sample_rate; - metadata.frame_rate = codec_ctx->frame_rate; - metadata.audio_codec = codec_ctx->audio_codec_id; - ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_INIT, metadata); - ngx_rtmp_mp4_write_moov(s, b, metadata); - rc = ngx_write_file(&file, b->start, b->last-b->start, 0); - if (rc < 0) { + ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); + ngx_rtmp_mp4_write_moov(s, &b, &metadata); + + rc = ngx_write_file(&file, b.start, (size_t) (b.last - b.start), 0); + if (rc == NGX_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: writing video init failed"); } + ngx_close_file(file.fd); - ngx_memzero(&path, sizeof(path)); - ngx_snprintf(path, sizeof(path), "%Vinit-audio.dash",&ctx->stream); + /* init audio */ - file.fd = ngx_open_file(path, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + ngx_str_set(&file.name, "dash-init-audio"); + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4a") = 0; + + file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); if (file.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; } - file.log = s->connection->log; - b->pos = b->last = b->start; + b.pos = b.last = b.start; metadata.video = 0; metadata.audio = 1; - ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_INIT, metadata); - ngx_rtmp_mp4_write_moov(s, b, metadata); - rc = ngx_write_file(&file, b->start, b->last-b->start, 0); - if (rc < 0) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: writing video init failed"); - } - ngx_close_file(file.fd); - ctx->buffer = b; + ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); + ngx_rtmp_mp4_write_moov(s, &b, &metadata); + + rc = ngx_write_file(&file, b.start, (size_t) (b.last - b.start), 0); + if (rc == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: writing audio init failed"); + } + + ngx_close_file(file.fd); return NGX_OK; } -static ngx_int_t -ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) +static void +ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, + ngx_uint_t sample_rate) { - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_buf_t *b, file_b; - ssize_t written = 0, size, write_size; - ngx_file_t file; - ngx_int_t rc; - ngx_rtmp_mp4_metadata_t metadata; - u_char *pos, *pos1; - ngx_rtmp_dash_frag_t *f; + u_char *pos, *pos1; + size_t left; + ssize_t n; + ngx_buf_t b; + ngx_file_t file; + ngx_rtmp_dash_ctx_t *ctx; - static u_char buffer[4096]; + 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->opened) { - return NGX_OK; + if (!t->opened) { + return; } - b = ctx->buffer; + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + b.start = buffer; + b.end = buffer + sizeof(buffer); + b.pos = b.last = b.start; - /* rewrite video segment */ - ngx_memzero(&buffer, sizeof(buffer)); - file_b.start = buffer; - file_b.end = file_b.start + sizeof(buffer); - file_b.pos = file_b.start; - file_b.last = file_b.pos; + ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_SEG, NULL); - b->pos = b->last = b->start; + pos = b.last; + b.last += 44; /* leave room for sidx */ - ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata); - pos = b->last; - b->last += 44; /* leave room for sidx */ - ngx_rtmp_mp4_write_moof(b, f->video_earliest_pres_time, - ctx->video_sample_count, ctx->video_sample_sizes, - (ctx->nfrags+ctx->frag),0); - pos1 = b->last; - b->last = pos; - ngx_rtmp_mp4_write_sidx(s, b, ctx->video_mdat_size+8+(pos1-(pos+44)), - f->video_earliest_pres_time, - f->video_latest_pres_time,0); - b->last = pos1; - ngx_rtmp_mp4_write_mdat(b, ctx->video_mdat_size+8); + ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, + t->sample_sizes, ctx->id, 0); + pos1 = b.last; + b.last = pos; + + ngx_rtmp_mp4_write_sidx(s, &b, t->mdat_size + 8 + (pos1 - (pos + 44)), + t->earliest_pres_time, t->latest_pres_time, + sample_rate); + b.last = pos1; + ngx_rtmp_mp4_write_mdat(&b, t->mdat_size + 8); /* move the data down to make room for the headers */ - size = (ssize_t) ctx->video_mdat_size; - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "v-temp") = 0; ngx_memzero(&file, sizeof(file)); + file.log = s->connection->log; + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%ui.m4%c", + t->id, t->type) = 0; + file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (file.fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: error creating dash temp video file"); - return NGX_ERROR; + goto done; } - ngx_str_set(&file.name, "temp"); + if (t->type == 'v') { + ngx_str_set(&file.name, "dash-video"); + } else { + ngx_str_set(&file.name, "dash-audio"); + } - ctx->video_file.offset = 0; - ngx_rtmp_mp4_write_data(s, &file, b); - do { - file_b.pos = file_b.last = file_b.start; - if ((ssize_t)(written + sizeof(buffer)) > size) { - ngx_read_file(&ctx->video_file, file_b.start, size-written, - ctx->video_file.offset); - file_b.last += size-written; - } - else { - ngx_read_file(&ctx->video_file, file_b.start, sizeof(buffer), - ctx->video_file.offset); - file_b.last += sizeof(buffer); - } - write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); - if (write_size == 0) { + if (ngx_write_file(&file, b.pos, (size_t) (b.last - b.pos), 0) == NGX_ERROR) + { + goto done; + } + + t->file.offset = 0; + left = (size_t) t->mdat_size; + + while (left > 0) { + + n = ngx_read_file(&t->file, buffer, ngx_min(sizeof(buffer), left), + t->file.offset); + + if (n == NGX_ERROR) { break; } - written += write_size; - } while (written < size); - ngx_close_file(ctx->video_file.fd); - ngx_close_file(file.fd); - rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->video_fragment.data); - if (rc != NGX_OK) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%s'->'%s'",ctx->stream.data, - ctx->video_fragment.data); - return NGX_ERROR; - } - - /* rewrite audio segment */ - written = 0; - ngx_memzero(&buffer, sizeof(buffer)); - file_b.pos = file_b.last = file_b.start; - b->pos = b->last = b->start; - ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata); - pos = b->last; - b->last += 44; /* leave room for sidx */ - ngx_rtmp_mp4_write_moof(b, f->audio_earliest_pres_time, - ctx->audio_sample_count, ctx->audio_sample_sizes, - (ctx->nfrags+ctx->frag), codec_ctx->sample_rate); - pos1 = b->last; - b->last = pos; - ngx_rtmp_mp4_write_sidx(s, b, ctx->audio_mdat_size+8+(pos1-(pos+44)), - f->audio_earliest_pres_time, - f->audio_latest_pres_time, codec_ctx->sample_rate); - b->last = pos1; - ngx_rtmp_mp4_write_mdat(b, ctx->audio_mdat_size+8); - - /* move the data down to make room for the headers */ - size = (ssize_t) ctx->audio_mdat_size; - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "a-temp") = 0; - - ngx_memzero(&file, sizeof(file)); - file.log = s->connection->log; - file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - - if (file.fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error creating temp audio file"); - return NGX_ERROR; - } - - ngx_str_set(&file.name, "temp"); - - ctx->audio_file.offset = 0; - ngx_rtmp_mp4_write_data(s, &file, b); - do { - file_b.pos = file_b.last = file_b.start; - if ((ssize_t)(written + sizeof(buffer)) > size) { - ngx_read_file(&ctx->audio_file, file_b.start, size-written, - ctx->audio_file.offset); - file_b.last += size-written; - } - else { - ngx_read_file(&ctx->audio_file, file_b.start, sizeof(buffer), - ctx->audio_file.offset); - file_b.last += sizeof(buffer); - } - write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); - if (write_size == 0) { + n = ngx_write_file(&file, buffer, (size_t) n, file.offset); + if (n == NGX_ERROR) { break; } - written += write_size; - } while (written < size); - ngx_close_file(ctx->audio_file.fd); - ngx_close_file(file.fd); - rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->audio_fragment.data); - if (rc != NGX_OK) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: rename failed: '%s'->'%s'",ctx->stream.data, - ctx->audio_fragment.data); - return NGX_ERROR; + left -= n; } - return NGX_OK; +done: + + if (file.fd != NGX_INVALID_FILE) { + ngx_close_file(file.fd); + } + + ngx_close_file(t->file.fd); + + t->opened = 0; } static ngx_int_t ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) { - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_app_conf_t *hacf; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_codec_ctx_t *codec_ctx; + + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: close fragments"); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); + if (!ctx->opened) { return NGX_OK; } - ngx_rtmp_dash_rewrite_segments(s); + ngx_rtmp_dash_close_fragment(s, &ctx->video, 0); + ngx_rtmp_dash_close_fragment(s, &ctx->audio, codec_ctx->sample_rate); ctx->opened = 0; - - if (ctx->nfrags == hacf->winfrags) { - ctx->frag++; - } else { - ctx->nfrags++; - } + ctx->id++; ngx_rtmp_dash_write_playlist(s); 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; + } + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); + + ngx_memzero(&t->file, sizeof(ngx_file_t)); + + t->file.log = s->connection->log; + + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%ui.m4%c.raw", + id, type) = 0; + + if (type == 'v') { + ngx_str_set(&t->file.name, "dash-raw-video"); + } else { + ngx_str_set(&t->file.name, "dash-raw-audio"); + } + + t->file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + + if (t->file.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; + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) { - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_frag_t *f; + 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; } - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); - f->id = (ctx->frag+ctx->nfrags); - - ngx_memzero(&ctx->video_file, sizeof(ctx->video_file)); - ngx_memzero(&ctx->audio_file, sizeof(ctx->audio_file)); - - ctx->video_file.log = s->connection->log; - ctx->audio_file.log = s->connection->log; - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4v", f->id) = 0; - *ngx_sprintf(ctx->video_fragment.data + ctx->stream.len, "%uL.m4v", - f->id) = 0; - - ngx_str_set(&ctx->video_file.name, "dash-v"); - ctx->video_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - if (ctx->video_file.fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error creating video fragment file"); - return NGX_ERROR; - } - - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4a", f->id) = 0; - *ngx_sprintf(ctx->audio_fragment.data + ctx->stream.len, "%uL.m4a", - f->id) = 0; - ngx_str_set(&ctx->audio_file.name, "dash-a"); - ctx->audio_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, - NGX_FILE_DEFAULT_ACCESS); - - if (ctx->audio_file.fd == NGX_INVALID_FILE) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: error creating audio fragment file"); - return NGX_ERROR; - } - - ctx->video_sample_count = 0; - f->video_earliest_pres_time = 0; - ctx->video_mdat_size = 0; - - ctx->audio_sample_count = 0; - f->audio_earliest_pres_time = 0; - ctx->audio_mdat_size = 0; + 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; @@ -674,13 +617,13 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) static ngx_int_t ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { - ngx_rtmp_dash_app_conf_t *hacf; - ngx_rtmp_dash_ctx_t *ctx; - u_char *p; - size_t len; + u_char *p; + size_t len; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - if (hacf == NULL || !hacf->dash || hacf->path.len == 0) { + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + if (dacf == NULL || !dacf->dash || dacf->path.len == 0) { goto next; } @@ -689,28 +632,20 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) } ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: publish: name='%s' type='%s'", - v->name, v->type); + "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); } - if (ctx->frags == NULL) { - ctx->frags = ngx_pcalloc(s->connection->pool, - sizeof(ngx_rtmp_dash_frag_t) * - (hacf->winfrags * 2 + 1)); - if (ctx->frags == NULL) { - return NGX_ERROR; - } - ctx->frag = 0; - ctx->nfrags = 0; - } + ctx->id = 0; if (ngx_strstr(v->name, "..")) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, @@ -727,10 +662,10 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) *ngx_cpymem(ctx->name.data, v->name, ctx->name.len) = 0; - len = hacf->path.len + 1 + ctx->name.len + sizeof(".mpd"); + len = dacf->path.len + 1 + ctx->name.len + sizeof(".mpd"); ctx->playlist.data = ngx_palloc(s->connection->pool, len); - p = ngx_cpymem(ctx->playlist.data, hacf->path.data, hacf->path.len); + p = ngx_cpymem(ctx->playlist.data, dacf->path.data, dacf->path.len); if (p[-1] != '/') { *p++ = '/'; @@ -738,39 +673,19 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) p = ngx_cpymem(p, ctx->name.data, ctx->name.len); - /* ctx->stream_path holds initial part of stream file path + /* ctx->playlist 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_INT64_LEN + - sizeof(".mp4")); - + ctx->stream.len + NGX_INT_T_LEN + + sizeof("xxxx.m4x")); ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1); ctx->stream.data[ctx->stream.len - 1] = '-'; - ctx->video_fragment.len = p - ctx->playlist.data + 1; - ctx->video_fragment.data = ngx_palloc(s->connection->pool, - ctx->video_fragment.len + NGX_INT64_LEN + - sizeof(".m4v")); - ctx->audio_fragment.len = p - ctx->playlist.data + 1; - ctx->audio_fragment.data = ngx_palloc(s->connection->pool, - ctx->audio_fragment.len + NGX_INT64_LEN + - sizeof(".m4a")); - - ngx_memcpy(ctx->video_fragment.data, ctx->playlist.data, - ctx->video_fragment.len - 1); - ctx->video_fragment.data[ctx->video_fragment.len - 1] = '-'; - ngx_memcpy(ctx->audio_fragment.data, ctx->playlist.data, - ctx->audio_fragment.len - 1); - ctx->audio_fragment.data[ctx->audio_fragment.len - 1] = '-'; - - /* playlist path */ p = ngx_cpymem(p, ".mpd", sizeof(".mpd") - 1); - ctx->playlist.len = p - ctx->playlist.data; - *p = 0; /* playlist bak (new playlist) path */ @@ -791,11 +706,13 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) /* start time for mpd */ ctx->start_time.data = ngx_palloc(s->connection->pool, - ngx_cached_http_log_iso8601.len); + ngx_cached_http_log_iso8601.len); ngx_memcpy(ctx->start_time.data, ngx_cached_http_log_iso8601.data, - ngx_cached_http_log_iso8601.len); + ngx_cached_http_log_iso8601.len); ctx->start_time.len = ngx_cached_http_log_iso8601.len; + ngx_rtmp_dash_open_fragments(s); + next: return next_publish(s, v); } @@ -804,14 +721,14 @@ next: static ngx_int_t ngx_rtmp_dash_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) { - ngx_rtmp_dash_app_conf_t *hacf; - ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_ctx_t *ctx; + ngx_rtmp_dash_app_conf_t *dacf; - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - if (hacf == NULL || !hacf->dash || ctx == NULL) { + if (dacf == NULL || !dacf->dash || ctx == NULL) { goto next; } @@ -827,163 +744,145 @@ next: static void ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, - uint32_t ts) + uint32_t timestamp) { - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_dash_app_conf_t *hacf; - int32_t duration; - ngx_rtmp_dash_frag_t *f; - - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + ngx_int_t hit; + 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); - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + hit = (timestamp >= ctx->fragment_end); - duration = ctx->video ? (f->video_latest_pres_time - - f->video_earliest_pres_time) : - (f->audio_latest_pres_time - - f->audio_earliest_pres_time); - - if ((ctx->video) && ((int32_t)(hacf->fraglen - duration) > 150)) { + if (ctx->has_video && !hit) { boundary = 0; } - if (!ctx->video && ctx->audio) { - if ((int32_t)(hacf->fraglen - duration) > 0) { - boundary = 0; - } - else { - boundary = 1; - } + + if (!ctx->has_video && ctx->has_audio) { + boundary = hit; } - if (ctx->nfrags == 0) { + if (ctx->id == 0) { ngx_rtmp_dash_write_init_segments(s); + ctx->fragment_end = timestamp; boundary = 1; } - if (boundary) { - f->SAP = 1; - } - - if (ctx->audio_mdat_size >= NGX_RTMP_DASH_MAX_SIZE) { + if (ctx->audio.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) { boundary = 1; } - if (ctx->video_mdat_size >= NGX_RTMP_DASH_MAX_SIZE) { + + if (ctx->video.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) { boundary = 1; } if (boundary) { ngx_rtmp_dash_close_fragments(s); ngx_rtmp_dash_open_fragments(s); + ctx->fragment_end += dacf->fraglen; } } +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 boundary, uint32_t timestamp) +{ + u_char *p; + size_t size, bsize; + + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + + if (!t->opened) { + return NGX_OK; + } + + 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, boundary, 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_file(&t->file, buffer, size, t->file.offset) == NGX_ERROR) + { + return NGX_ERROR; + } + + t->sample_sizes[t->sample_count++] = (uint32_t) size; + 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) { - ngx_rtmp_dash_app_conf_t *hacf; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - size_t bsize; - ngx_buf_t out; - ngx_rtmp_dash_frag_t *f; - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + 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 (hacf == NULL || !hacf->dash || ctx == NULL || - codec_ctx == NULL || h->mlen < 2) + if (dacf == NULL || !dacf->dash || ctx == NULL || + codec_ctx == NULL || h->mlen < 2) { return NGX_OK; } - /* Disable MP3 for now - if ((codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC) && - (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_MP3)) */ - if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC) - { + if (!ctx->opened) { return NGX_OK; } - if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC && + /* Only AAC is supported */ + + if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC || codec_ctx->aac_header == NULL) { return NGX_OK; } - ngx_memzero(&out, sizeof(out)); - - out.start = buffer; - out.end = buffer + sizeof(buffer); - out.pos = out.start; - out.last = out.pos; - - /* copy payload */ - - ctx->audio = 1; - - if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_MP3) { - in->buf->pos += 1; + if (in->buf->last - in->buf->pos < 2) { + return NGX_ERROR; } - for (; in && out.last < out.end; in = in->next) { - bsize = in->buf->last - in->buf->pos; - if (bsize < 4) { - continue; - } - if (out.last + bsize > out.end) { - bsize = out.end - out.last; - } - if (codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC && *in->buf->pos == 0xAF) { /* rtmp frame header */ - /* rtmp audio frame number--skip 0 */ - if (*(in->buf->pos+1) == 0x00) { - break; - } - else { - if (bsize > 3) { - /* skip two bytes of audio frame header */ - in->buf->pos += 2; - /* skip the extra mp3 byte */ - } - } - } - if (!in->next && codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC) { - bsize -= 2; /* chop 2 bytes off the end of the frame */ - } - out.last = ngx_cpymem(out.last, in->buf->pos, bsize); + /* skip AAC config */ + + htype = in->buf->pos[1]; + if (htype != 1) { + return NGX_OK; } - if (out.last-out.pos > 0) { - ngx_rtmp_dash_update_fragments(s, 0, h->timestamp); + ctx->has_audio = 1; - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); + /* skip RTMP & AAC headers */ - /* Set Presentation Times */ - if (ctx->audio_sample_count == 0 ) { - f->audio_earliest_pres_time = h->timestamp; - } - f->audio_latest_pres_time = h->timestamp; + in->buf->pos += 2; - ctx->audio_sample_count += 1; - if ((ctx->audio_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES)) { - ctx->audio_sample_sizes[ctx->audio_sample_count] = - ngx_rtmp_mp4_write_data(s, &ctx->audio_file, &out); - ctx->audio_mdat_size += - ctx->audio_sample_sizes[ctx->audio_sample_count]; - } - else { - ctx->audio_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; - } - } - - return NGX_OK; + return ngx_rtmp_dash_append(s, in, &ctx->audio, 0, h->timestamp); } @@ -991,126 +890,58 @@ static ngx_int_t ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) { - ngx_rtmp_dash_app_conf_t *hacf; - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; - u_char *p; - uint8_t htype, fmt, ftype; - uint32_t i = 1; - ngx_buf_t out; - size_t bsize; - ngx_rtmp_dash_frag_t *f; - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); + uint8_t ftype, 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 (hacf == NULL || !hacf->dash || ctx == NULL || codec_ctx == NULL || - codec_ctx->avc_header == NULL || h->mlen < 1) + 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; - } - - ctx->video = 1; - - if (in->buf->last-in->buf->pos < 2) { - return NGX_ERROR; - } - - /* 1: keyframe (IDR) - * 2: inter frame - * 3: disposable inter frame */ - - ngx_memcpy(&fmt, in->buf->pos, 1); - ftype = (fmt & 0xf0) >> 4; - - /* proceed only with PICT */ - - ngx_memcpy(&htype, in->buf->pos+1, 1); - if (htype != 1) { - return NGX_OK; - } - - ngx_memzero(&out, sizeof(out)); - out.start = buffer; - out.end = buffer + sizeof(buffer); - out.pos = out.start; - out.last = out.pos; - - ngx_rtmp_dash_update_fragments(s, (ftype ==1), h->timestamp); - - f = ngx_rtmp_dash_get_frag(s, ctx->nfrags); - if (!ctx->opened) { return NGX_OK; } - /* Set presentation times */ - if (ctx->video_sample_count == 0) { - f->video_earliest_pres_time = h->timestamp; - } - f->video_latest_pres_time = h->timestamp; + /* Only H264 is supported */ - for (; in && out.last < out.end; in = in->next) { - p = in->buf->pos; - if (i == 1) { - i = 2; - p += 5; - } - bsize = in->buf->last - p; - if (out.last + bsize > out.end) { - bsize = out.end - out.last; - } - - out.last = ngx_cpymem(out.last, p, bsize); + if (codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) { + return NGX_OK; } - ctx->video_sample_count += 1; - if (ctx->video_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES) { - ctx->video_sample_sizes[ctx->video_sample_count] = - ngx_rtmp_mp4_write_data(s, &ctx->video_file, &out); - ctx->video_mdat_size += - ctx->video_sample_sizes[ctx->video_sample_count]; - } - else { - ctx->video_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; + if (in->buf->last - in->buf->pos < 5) { + return NGX_ERROR; } - return NGX_OK; -} + ftype = (in->buf->pos[0] & 0xf0) >> 4; + /* skip AVC config */ -static void -ngx_rtmp_dash_discontinue(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) { - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: discontinue"); - - ngx_close_file(ctx->video_file.fd); - ngx_close_file(ctx->audio_file.fd); - ctx->opened = 0; + htype = in->buf->pos[1]; + if (htype != 1) { + return NGX_OK; } + + 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); } static ngx_int_t ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) { - ngx_rtmp_dash_discontinue(s); + ngx_rtmp_dash_open_fragments(s); return next_stream_begin(s, v); } @@ -1119,7 +950,7 @@ ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) static ngx_int_t ngx_rtmp_dash_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v) { - ngx_rtmp_dash_discontinue(s); + ngx_rtmp_dash_close_fragments(s); return next_stream_eof(s, v); } @@ -1128,21 +959,20 @@ ngx_rtmp_dash_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *v) static ngx_int_t ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) { - ngx_dir_t dir; - time_t mtime, max_age; - ngx_err_t err; - ngx_str_t name, spath; - u_char *p; - ngx_int_t nentries, nerased; - u_char path[NGX_MAX_PATH + 1]; + time_t mtime, max_age; + u_char *p; + u_char path[NGX_MAX_PATH + 1]; + ngx_dir_t dir; + ngx_err_t err; + ngx_str_t name, spath; + ngx_int_t nentries, nerased; ngx_log_debug2(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, - "dash: cleanup path='%V' playlen=%M", - ppath, playlen); + "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); + "dash: cleanup open dir failed '%V'", ppath); return NGX_ERROR; } @@ -1216,9 +1046,9 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) } 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') + name.data[name.len - 3] == 'm' && + name.data[name.len - 2] == '4' && + name.data[name.len - 1] == 'v') { max_age = playlen / 166; @@ -1235,6 +1065,14 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) name.data[name.len - 1] == 'd') { max_age = playlen / 166; + + } 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 / 166; + } else { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "dash: cleanup skip unknown file type '%V'", &name); @@ -1290,6 +1128,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) return conf; } + static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) { @@ -1343,11 +1182,12 @@ ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) return NGX_CONF_OK; } + static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf) { - ngx_rtmp_core_main_conf_t *cmcf; - ngx_rtmp_handler_pt *h; + ngx_rtmp_handler_pt *h; + ngx_rtmp_core_main_conf_t *cmcf; cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 9c8ac8a..8d010f1 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -6,12 +6,23 @@ #include +/* tie resolution */ +#define NGX_RTMP_MP4_TIMESCALE 1000 + +/* normal forward playback as defined by spec */ +#define NGX_RTMP_MP4_PREFERRED_RATE 0x00010000 + +/* full volume as defined by spec */ +#define NGX_RTMP_MP4_PREFERRED_VOLUME 0x0100 + + static u_char compressor_name[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; -ngx_int_t +/* +static ngx_int_t ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) { u_char bytes[8]; @@ -25,13 +36,17 @@ ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) bytes[6] = ((uint64_t)n >> 8) & 0xFF; bytes[7] = (uint64_t)n & 0xFF; + if (b->last + sizeof(bytes) > b->end) { + return NGX_ERROR; + } + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); return NGX_OK; } +*/ - -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) { u_char bytes[4]; @@ -41,13 +56,17 @@ ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) bytes[2] = ((uint32_t)n >> 8) & 0xFF; bytes[3] = (uint32_t)n & 0xFF; + if (b->last + sizeof(bytes) > b->end) { + return NGX_ERROR; + } + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); return NGX_OK; } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) { u_char bytes[3]; @@ -56,13 +75,17 @@ ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) bytes[1] = ((uint32_t)n >> 8) & 0xFF; bytes[2] = (uint32_t)n & 0xFF; + if (b->last + sizeof(bytes) > b->end) { + return NGX_ERROR; + } + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); return NGX_OK; } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) { u_char bytes[2]; @@ -70,26 +93,34 @@ ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) bytes[0] = ((uint32_t)n >> 8) & 0xFF; bytes[1] = (uint32_t)n & 0xFF; + if (b->last + sizeof(bytes) > b->end) { + return NGX_ERROR; + } + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); return NGX_OK; } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) { u_char bytes[1]; bytes[0] = n & 0xFF; + if (b->last + sizeof(bytes) > b->end) { + return NGX_ERROR; + } + b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); return NGX_OK; } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { //int i = 3; @@ -105,7 +136,7 @@ ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) { u_char *curpos; @@ -125,9 +156,9 @@ ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) |a b u| |c d v| |tx ty w| */ -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, - int16_t d, int16_t tx, int16_t ty) + int16_t d, int16_t tx, int16_t ty) { ngx_rtmp_mp4_field_32(buf, a << 16); /* 16.16 format */ ngx_rtmp_mp4_field_32(buf, b << 16); /* 16.16 format */ @@ -145,7 +176,7 @@ ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -154,25 +185,32 @@ ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - if (type == NGX_RTMP_MP4_FILETYPE_INIT) { - b->last = ngx_cpymem(b->last, "ftypiso5", sizeof("ftypiso5")-1); + switch (type) { + + case NGX_RTMP_MP4_FILETYPE_INIT: + + b->last = ngx_cpymem(b->last, "ftypiso5", sizeof("ftypiso5") - 1); ngx_rtmp_mp4_field_32(b, 1); - if (metadata.video == 1) { + + if (metadata != NULL && metadata->video == 1) { b->last = ngx_cpymem(b->last, "avc1iso5dash", - sizeof("avc1iso5dash")-1); + sizeof("avc1iso5dash") - 1); + } else { + b->last = ngx_cpymem(b->last, "iso5dash", sizeof("iso5dash") - 1); } - else { - b->last = ngx_cpymem(b->last, "iso5dash", sizeof("iso5dash")-1); - } - - } - if (type == NGX_RTMP_MP4_FILETYPE_SEG) { - b->last = ngx_cpymem(b->last, "stypmsdh", sizeof("stypmsdh")-1); + + break; + + case NGX_RTMP_MP4_FILETYPE_SEG: + + b->last = ngx_cpymem(b->last, "stypmsdh", sizeof("stypmsdh") - 1); ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "msdhmsix", sizeof("msdhmsix")-1); + b->last = ngx_cpymem(b->last, "msdhmsix", sizeof("msdhmsix") - 1); + + break; } ngx_rtmp_mp4_update_box_size(b, pos); @@ -181,8 +219,8 @@ ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, } -ngx_int_t -ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +static ngx_int_t +ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -221,8 +259,8 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) } -ngx_int_t -ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +static ngx_int_t +ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -243,15 +281,15 @@ ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ /* 2 16s, layer and alternate group */ - ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0x00000001 : 0); - ngx_rtmp_mp4_field_16(b, metadata.audio == 1 ? 0x0100 : 0); + ngx_rtmp_mp4_field_32(b, metadata->audio == 1 ? 0x00000001 : 0); + ngx_rtmp_mp4_field_16(b, metadata->audio == 1 ? 0x0100 : 0); ngx_rtmp_mp4_field_16(b, 0); /* reserved */ ngx_rtmp_mp4_write_matrix(b, 1, 0, 0, 1, 0, 0); - if (metadata.video == 1) { - ngx_rtmp_mp4_field_32(b, metadata.width << 16); /* width */ - ngx_rtmp_mp4_field_32(b, metadata.height << 16); /* height */ + if (metadata->video == 1) { + ngx_rtmp_mp4_field_32(b, metadata->width << 16); /* width */ + ngx_rtmp_mp4_field_32(b, metadata->height << 16); /* height */ } else { ngx_rtmp_mp4_field_32(b, 0); /* not relevant for audio */ @@ -264,8 +302,8 @@ ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) } -ngx_int_t -ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +static ngx_int_t +ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -280,8 +318,8 @@ ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* creation time */ ngx_rtmp_mp4_field_32(b, 0); /* modification time */ /* time scale*/ - ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? metadata.sample_rate : - NGX_RTMP_MP4_TIMESCALE); + ngx_rtmp_mp4_field_32(b, metadata->audio == 1 ? metadata->sample_rate : + NGX_RTMP_MP4_TIMESCALE); ngx_rtmp_mp4_field_32(b, 0); /* duration */ ngx_rtmp_mp4_field_16(b, 0x15C7); /* language */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ @@ -292,8 +330,8 @@ ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) } -ngx_int_t -ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +static ngx_int_t +ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -306,7 +344,7 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* version and flags */ ngx_rtmp_mp4_field_32(b, 0); /* pre defined (=0) */ - if (metadata.video == 1) { + if (metadata->video == 1) { /* video handler */ b->last = ngx_cpymem(b->last, "vide", sizeof("vide")-1); } @@ -319,7 +357,7 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - if (metadata.video == 1) { + if (metadata->video == 1) { /* video handler string--NULL TERMINATED */ b->last = ngx_cpymem(b->last, "VideoHandler", sizeof("VideoHandler")); } @@ -334,7 +372,7 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_vmhd(ngx_buf_t *b) { /* size is always 0x14, apparently */ @@ -350,7 +388,7 @@ ngx_rtmp_mp4_write_vmhd(ngx_buf_t *b) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) { /* size is always 16, apparently */ @@ -366,8 +404,8 @@ ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) } -ngx_int_t -ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +static ngx_int_t +ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -392,8 +430,8 @@ ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) } -ngx_int_t -ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +static ngx_int_t +ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -412,9 +450,9 @@ ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos, *p; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -458,9 +496,9 @@ ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -479,8 +517,8 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_16(b, metadata.width); - ngx_rtmp_mp4_field_16(b, metadata.height); + ngx_rtmp_mp4_field_16(b, metadata->width); + ngx_rtmp_mp4_field_16(b, metadata->height); ngx_rtmp_mp4_field_32(b, 0x00480000); /* Horizontal resolution 72dpi */ ngx_rtmp_mp4_field_32(b, 0x00480000); /* Vertical resolution 72dpi */ ngx_rtmp_mp4_field_32(b, 0); /* Data size (= 0) */ @@ -506,9 +544,9 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -546,7 +584,7 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_8(b, 0x00); /* flags */ /* length of the rest of the box */ ngx_rtmp_mp4_put_descr(b, 0x04, 13+decoder_info); - ngx_rtmp_mp4_field_8(b, metadata.audio_codec == NGX_RTMP_AUDIO_AAC ? 0x40 : + ngx_rtmp_mp4_field_8(b, metadata->audio_codec == NGX_RTMP_AUDIO_AAC ? 0x40 : 0x6B); /* codec id */ ngx_rtmp_mp4_field_8(b, 0x15); /* audio stream */ ngx_rtmp_mp4_field_24(b, 0); /* buffersize? */ @@ -556,7 +594,8 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, if (aac) { ngx_rtmp_mp4_put_descr(b, 0x05, decoder_info); - b->last = ngx_cpymem(b->last, aac->buf->pos+aac_header_offset, decoder_info); + b->last = ngx_cpymem(b->last, aac->buf->pos+aac_header_offset, + decoder_info); } ngx_rtmp_mp4_put_descr(b, 0x06, 1); @@ -568,9 +607,9 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -591,7 +630,7 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_16(b, 16); /* something mp4 specific */ ngx_rtmp_mp4_field_16(b, 0); /* something mp4 specific */ ngx_rtmp_mp4_field_16(b, 0); /* packet size (=0) */ - ngx_rtmp_mp4_field_16(b, metadata.sample_rate); /* sample rate */ + ngx_rtmp_mp4_field_16(b, metadata->sample_rate); /* sample rate */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ ngx_rtmp_mp4_write_esds(s, b, metadata); @@ -604,9 +643,9 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -620,7 +659,7 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ ngx_rtmp_mp4_field_32(b, 1); /* entry count */ - if (metadata.video == 1) { + if (metadata->video == 1) { ngx_rtmp_mp4_write_video(s,b,metadata); } else { @@ -633,7 +672,7 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_stts(ngx_buf_t *b) { u_char *pos; @@ -654,7 +693,7 @@ ngx_rtmp_mp4_write_stts(ngx_buf_t *b) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) { u_char *pos; @@ -675,7 +714,7 @@ ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) { u_char *pos; @@ -697,7 +736,7 @@ ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_stco(ngx_buf_t *b) { u_char *pos; @@ -718,9 +757,9 @@ ngx_rtmp_mp4_write_stco(ngx_buf_t *b) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -743,9 +782,9 @@ ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -756,7 +795,7 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, b->last = ngx_cpymem(b->last, "minf", sizeof("minf")-1); - if (metadata.video == 1) { + if (metadata->video == 1) { ngx_rtmp_mp4_write_vmhd(b); } else { @@ -772,9 +811,9 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -794,9 +833,9 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, return NGX_OK; } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -816,18 +855,18 @@ ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t -ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) +static ngx_int_t +ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; uint32_t sample_dur; - if (metadata.video == 1) { - sample_dur = metadata.frame_rate > 0 ? NGX_RTMP_MP4_TIMESCALE / - metadata.frame_rate : NGX_RTMP_MP4_TIMESCALE; + if (metadata->video == 1) { + sample_dur = metadata->frame_rate > 0 ? NGX_RTMP_MP4_TIMESCALE / + metadata->frame_rate : NGX_RTMP_MP4_TIMESCALE; } else { - sample_dur = metadata.audio_codec == NGX_RTMP_AUDIO_AAC ? 1024 : 1152; + sample_dur = metadata->audio_codec == NGX_RTMP_AUDIO_AAC ? 1024 : 1152; } pos = b->last; @@ -856,7 +895,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_rtmp_mp4_field_32(b, sample_dur); /* default sample duration */ ngx_rtmp_mp4_field_32(b, 0); /* default sample size */ /* default sample flags */ - ngx_rtmp_mp4_field_32(b, metadata.audio == 1 ? 0 : 0x00010000); + ngx_rtmp_mp4_field_32(b, metadata->audio == 1 ? 0 : 0x00010000); ngx_rtmp_mp4_update_box_size(b, pos); @@ -866,7 +905,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata) + ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; @@ -887,7 +926,7 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) { u_char *pos; @@ -912,9 +951,9 @@ ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - ngx_uint_t sample_rate) + ngx_uint_t sample_rate) { u_char *pos; float multiplier; @@ -943,10 +982,9 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, - uint32_t sample_sizes[128], u_char *moof_pos, - ngx_uint_t sample_rate) + uint32_t sample_sizes[128], u_char *moof_pos, ngx_uint_t sample_rate) { u_char *pos; uint32_t i, offset; @@ -986,10 +1024,10 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, uint32_t sample_sizes[128], - u_char *moof_pos, ngx_uint_t sample_rate) + uint32_t sample_count, uint32_t sample_sizes[128], u_char *moof_pos, + ngx_uint_t sample_rate) { u_char *pos; @@ -1011,7 +1049,7 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, } -ngx_int_t +static ngx_int_t ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) { u_char *pos; @@ -1034,9 +1072,8 @@ ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_uint_t reference_size, - ngx_uint_t earliest_pres_time, - ngx_uint_t latest_pres_time, ngx_uint_t sample_rate) + ngx_uint_t reference_size, ngx_uint_t earliest_pres_time, + ngx_uint_t latest_pres_time, ngx_uint_t sample_rate) { u_char *pos; uint32_t ept, dur; @@ -1086,8 +1123,8 @@ ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, uint32_t sample_sizes[128], - uint32_t index, ngx_uint_t sample_rate) + uint32_t sample_count, uint32_t sample_sizes[128], uint32_t index, + ngx_uint_t sample_rate) { u_char *pos; @@ -1117,33 +1154,3 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) return NGX_OK; } - - -uint32_t -ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, ngx_buf_t *b) -{ - ngx_int_t rc; - uint32_t size; - - if (b == NULL) { - return 0; //error - } - - size = b->last-b->start; - if (size < 1) { - return 0; - } - - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: data written: %d", - (int) size); - - rc = ngx_write_file(file, b->start, size, file->offset); - if (rc < 0) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "dash: writing file failed"); - return 0; //error - } - - return size; -} diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 3226232..7f67d02 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -26,31 +26,17 @@ enum { }; -/* divide all times by this value. this is the same resolution as RTMP so it -is convenient */ -#define NGX_RTMP_MP4_TIMESCALE 1000 -/* normal forward playback as defined by spec */ -#define NGX_RTMP_MP4_PREFERRED_RATE 0x00010000 -/* full volume as defined by spec */ -#define NGX_RTMP_MP4_PREFERRED_VOLUME 0x0100 - -ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, - ngx_rtmp_mp4_metadata_t metadata); -ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t metadata); -ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, - ngx_uint_t earliest_pres_time, - uint32_t sample_count, - uint32_t sample_sizes[128], uint32_t index, - ngx_uint_t sample_rate); -ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_uint_t reference_size, - ngx_uint_t earliest_pres_time, - ngx_uint_t latest_pres_time, - ngx_uint_t sample_rate); +ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, + ngx_rtmp_mp4_metadata_t *metadata); +ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_rtmp_mp4_metadata_t *metadata); +ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, + uint32_t sample_count, uint32_t sample_sizes[128], uint32_t index, + ngx_uint_t sample_rate); +ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, + ngx_uint_t reference_size, ngx_uint_t earliest_pres_time, + ngx_uint_t latest_pres_time, ngx_uint_t sample_rate); ngx_uint_t ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size); -uint32_t ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, - ngx_buf_t *b); -#endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ \ No newline at end of file +#endif /* _NGX_RTMP_MP4_H_INCLUDED_ */ From 0e074927976846c29ab5cad1962deecf35407ff7 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Mon, 11 Nov 2013 13:57:02 +0400 Subject: [PATCH 10/50] fixed sample sizes in dash --- 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 8d010f1..2856786 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -1014,7 +1014,7 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, ngx_rtmp_mp4_field_32(b, 0); /* first sample flags */ } - for (i = 1; i <= sample_count; i++) { + for (i = 0; i < sample_count; i++) { ngx_rtmp_mp4_field_32(b, sample_sizes[i]); } From 6caee7cb88b1109803b48f65a7c510dfe3186186 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 14:38:08 +0400 Subject: [PATCH 11/50] working video --- dash/ngx_rtmp_dash_module.c | 199 +++++++++++++++++------------------- dash/ngx_rtmp_mp4.c | 46 ++++----- dash/ngx_rtmp_mp4.h | 11 +- 3 files changed, 124 insertions(+), 132 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 107b4f6..e8c1290 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -18,11 +18,12 @@ 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); #define NGX_RTMP_DASH_BUFSIZE (1024*1024) -#define NGX_RTMP_DASH_MAX_MDAT (800*1024) -#define NGX_RTMP_DASH_MAX_SAMPLES 512 +#define NGX_RTMP_DASH_MAX_MDAT (10*1024*1024) +#define NGX_RTMP_DASH_MAX_SAMPLES 1024 typedef struct { @@ -30,11 +31,11 @@ typedef struct { ngx_uint_t opened; ngx_uint_t mdat_size; ngx_uint_t sample_count; - ngx_file_t file; + ngx_fd_t fd; char type; uint32_t earliest_pres_time; uint32_t latest_pres_time; - uint32_t sample_sizes[NGX_RTMP_DASH_MAX_SAMPLES]; + ngx_rtmp_mp4_sample_t samples[NGX_RTMP_DASH_MAX_SAMPLES]; } ngx_rtmp_dash_track_t; @@ -53,7 +54,6 @@ typedef struct { ngx_file_t audio_file; ngx_uint_t id; - uint32_t fragment_end; ngx_rtmp_dash_track_t audio; ngx_rtmp_dash_track_t video; @@ -187,6 +187,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) 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); @@ -225,13 +229,13 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " sar=\"1:1\"\n" \ " startWithSAP=\"1\"\n" \ " bandwidth=\"%ui\">\n" \ - " \n" \ + " \n" \ " \n" \ " \n" @@ -278,7 +282,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->height, codec_ctx->frame_rate, (ngx_uint_t) (live_ctx->stream->bw_in.bandwidth * 8), - ctx->video.earliest_pres_time, + (ngx_uint_t) 0,//ctx->video.earliest_pres_time, (ngx_uint_t) dacf->fraglen, &ctx->name, &ctx->name); @@ -326,9 +330,9 @@ 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_fd_t fd; ngx_int_t rc; ngx_buf_t b; - ngx_file_t file; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_mp4_metadata_t metadata; @@ -342,9 +346,6 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) return NGX_ERROR; } - ngx_memzero(&file, sizeof(ngx_file_t)); - file.log = s->connection->log; - metadata.width = codec_ctx->width; metadata.height = codec_ctx->height; metadata.sample_rate = codec_ctx->sample_rate; @@ -353,13 +354,12 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) /* init video */ - ngx_str_set(&file.name, "dash-init-video"); *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4v") = 0; - file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - if (file.fd == NGX_INVALID_FILE) { + 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; @@ -375,23 +375,22 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); ngx_rtmp_mp4_write_moov(s, &b, &metadata); - rc = ngx_write_file(&file, b.start, (size_t) (b.last - b.start), 0); + 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(file.fd); + ngx_close_file(fd); /* init audio */ - ngx_str_set(&file.name, "dash-init-audio"); *ngx_sprintf(ctx->stream.data + ctx->stream.len, "init.m4a") = 0; - file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, - NGX_FILE_DEFAULT_ACCESS); + fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, + NGX_FILE_DEFAULT_ACCESS); - if (file.fd == NGX_INVALID_FILE) { + 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; @@ -405,13 +404,13 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); ngx_rtmp_mp4_write_moov(s, &b, &metadata); - rc = ngx_write_file(&file, b.start, (size_t) (b.last - b.start), 0); + 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(file.fd); + ngx_close_file(fd); return NGX_OK; } @@ -424,8 +423,8 @@ 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_file_t file; ngx_rtmp_dash_ctx_t *ctx; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; @@ -434,6 +433,9 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, return; } + ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: close fragment id=%ui, type=%c", t->id, t->type); + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); b.start = buffer; @@ -445,8 +447,11 @@ 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_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "dash: fragment earliest pts: %uD", t->earliest_pres_time); + ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, - t->sample_sizes, ctx->id, 0); + t->samples, t->id, sample_rate); pos1 = b.last; b.last = pos; @@ -458,46 +463,38 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, /* move the data down to make room for the headers */ - ngx_memzero(&file, sizeof(file)); - - file.log = s->connection->log; - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%ui.m4%c", t->id, t->type) = 0; - file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); + fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - if (file.fd == NGX_INVALID_FILE) { + 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 (t->type == 'v') { - ngx_str_set(&file.name, "dash-video"); - } else { - ngx_str_set(&file.name, "dash-audio"); - } - - if (ngx_write_file(&file, b.pos, (size_t) (b.last - b.pos), 0) == NGX_ERROR) - { + if (ngx_write_fd(fd, b.pos, (size_t) (b.last - b.pos)) == NGX_ERROR) { goto done; } - t->file.offset = 0; left = (size_t) t->mdat_size; + if (lseek(t->fd, 0, SEEK_SET) == -1) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "dash: lseek error"); + goto done; + } + while (left > 0) { - n = ngx_read_file(&t->file, buffer, ngx_min(sizeof(buffer), left), - t->file.offset); - + n = ngx_read_fd(t->fd, buffer, ngx_min(sizeof(buffer), left)); if (n == NGX_ERROR) { break; } - n = ngx_write_file(&file, buffer, (size_t) n, file.offset); + n = ngx_write_fd(fd, buffer, (size_t) n); if (n == NGX_ERROR) { break; } @@ -507,12 +504,13 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, done: - if (file.fd != NGX_INVALID_FILE) { - ngx_close_file(file.fd); + if (fd != NGX_INVALID_FILE) { + ngx_close_file(fd); } - ngx_close_file(t->file.fd); + ngx_close_file(t->fd); + t->fd = NGX_INVALID_FILE; t->opened = 0; } @@ -536,11 +534,11 @@ ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) ngx_rtmp_dash_close_fragment(s, &ctx->video, 0); ngx_rtmp_dash_close_fragment(s, &ctx->audio, codec_ctx->sample_rate); - ctx->opened = 0; - ctx->id++; - ngx_rtmp_dash_write_playlist(s); + ctx->id++; + ctx->opened = 0; + return NGX_OK; } @@ -557,23 +555,12 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - ngx_memzero(&t->file, sizeof(ngx_file_t)); + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "raw.m4%c", type) = 0; - t->file.log = s->connection->log; + t->fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, + NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%ui.m4%c.raw", - id, type) = 0; - - if (type == 'v') { - ngx_str_set(&t->file.name, "dash-raw-video"); - } else { - ngx_str_set(&t->file.name, "dash-raw-audio"); - } - - t->file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, - NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); - - if (t->file.fd == NGX_INVALID_FILE) { + 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; @@ -605,8 +592,15 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) return NGX_OK; } - ngx_rtmp_dash_open_fragment(s, &ctx->video, ctx->id, 'v'); - ngx_rtmp_dash_open_fragment(s, &ctx->audio, ctx->id, 'a'); + if (ctx->has_video) { + ngx_rtmp_dash_open_fragment(s, &ctx->video, ctx->id, 'v'); + } + + if (ctx->has_audio) { + ngx_rtmp_dash_open_fragment(s, &ctx->audio, ctx->id, 'a'); + } + + //ctx->id++; ctx->opened = 1; @@ -711,8 +705,6 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ngx_cached_http_log_iso8601.len); ctx->start_time.len = ngx_cached_http_log_iso8601.len; - ngx_rtmp_dash_open_fragments(s); - next: return next_publish(s, v); } @@ -753,7 +745,9 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - hit = (timestamp >= ctx->fragment_end); + /*TODO: non-zero initial timestamp */ + + hit = (timestamp >= (ctx->id + 1) * dacf->fraglen); if (ctx->has_video && !hit) { boundary = 0; @@ -763,12 +757,6 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, boundary = hit; } - if (ctx->id == 0) { - ngx_rtmp_dash_write_init_segments(s); - ctx->fragment_end = timestamp; - boundary = 1; - } - if (ctx->audio.mdat_size >= NGX_RTMP_DASH_MAX_MDAT) { boundary = 1; } @@ -780,7 +768,6 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, if (boundary) { ngx_rtmp_dash_close_fragments(s); ngx_rtmp_dash_open_fragments(s); - ctx->fragment_end += dacf->fraglen; } } @@ -789,14 +776,13 @@ 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 boundary, uint32_t timestamp) { - u_char *p; - size_t size, bsize; + u_char *p; + size_t size, bsize; + ngx_rtmp_dash_ctx_t *ctx; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - if (!t->opened) { - return NGX_OK; - } + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); p = buffer; size = 0; @@ -812,21 +798,38 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, size += bsize; } + t->latest_pres_time = timestamp; + + if (t->sample_count > 0) { + t->samples[t->sample_count - 1].duration = timestamp - + t->samples[t->sample_count - 1].timestamp; + } + + if (!ctx->opened) { + ngx_rtmp_dash_open_fragments(s); + } + ngx_rtmp_dash_update_fragments(s, boundary, 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_file(&t->file, buffer, size, t->file.offset) == NGX_ERROR) - { + + 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; } - t->sample_sizes[t->sample_count++] = (uint32_t) size; + t->samples[t->sample_count].delay = 0; + t->samples[t->sample_count].size = (uint32_t) size; + t->samples[t->sample_count].duration = 0; + t->samples[t->sample_count].timestamp = timestamp; + t->samples[t->sample_count].key = (boundary ? 1 : 0); + + t->sample_count++; t->mdat_size += (ngx_uint_t) size; } @@ -853,10 +856,6 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } - if (!ctx->opened) { - return NGX_OK; - } - /* Only AAC is supported */ if (codec_ctx->audio_codec_id != NGX_RTMP_AUDIO_AAC || @@ -905,10 +904,6 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } - if (!ctx->opened) { - return NGX_OK; - } - /* Only H264 is supported */ if (codec_ctx->video_codec_id != NGX_RTMP_VIDEO_H264) { @@ -941,8 +936,6 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, static ngx_int_t ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) { - ngx_rtmp_dash_open_fragments(s); - return next_stream_begin(s, v); } diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 2856786..45d9184 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -877,14 +877,14 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) b->last = ngx_cpymem(b->last, "mvex", sizeof("mvex")-1); /* just write the trex and mehd in here too */ - +#if 0 ngx_rtmp_mp4_field_32(b, 16); b->last = ngx_cpymem(b->last, "mehd", sizeof("mehd")-1); ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ ngx_rtmp_mp4_field_32(b, 0x000D8D2A); /* frag duration */ - +#endif ngx_rtmp_mp4_field_32(b, 0x20); b->last = ngx_cpymem(b->last, "trex", sizeof("trex")-1); @@ -984,38 +984,29 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, static ngx_int_t ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, - uint32_t sample_sizes[128], u_char *moof_pos, ngx_uint_t sample_rate) + ngx_rtmp_mp4_sample_t *samples, u_char *moof_pos) { - u_char *pos; - uint32_t i, offset; + u_char *pos; + uint32_t i, offset; pos = b->last; - if (sample_rate > 0) { - /* moof stuff + trun stuff + sample entries + mdat header */ - offset = (pos-moof_pos) + 20 + (sample_count*4) + 8; - } - else { - /* moof stuff + trun stuff + sample entries + mdat header */ - offset = (pos-moof_pos) + 24 + (sample_count*4) + 8; - } - + offset = (pos - moof_pos) + 20 + (sample_count * 4 * 4) + 8; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "trun", sizeof("trun")-1); + b->last = ngx_cpymem(b->last, "trun", sizeof("trun") - 1); - /* version and flags */ - ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00000201 : 0x00000205); - ngx_rtmp_mp4_field_32(b, sample_count); /* sample count */ - ngx_rtmp_mp4_field_32(b, offset); /* data offset */ - if (sample_rate == 0) { - ngx_rtmp_mp4_field_32(b, 0); /* first sample flags */ - } + ngx_rtmp_mp4_field_32(b, 0x00000f01); + ngx_rtmp_mp4_field_32(b, sample_count); + ngx_rtmp_mp4_field_32(b, offset); for (i = 0; i < sample_count; i++) { - ngx_rtmp_mp4_field_32(b, sample_sizes[i]); + ngx_rtmp_mp4_field_32(b, samples[i].duration); + ngx_rtmp_mp4_field_32(b, samples[i].size); + ngx_rtmp_mp4_field_32(b, samples[i].key ? 0x00000000 : 0x00010000); + ngx_rtmp_mp4_field_32(b, samples[i].delay); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -1026,7 +1017,7 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, static ngx_int_t ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, uint32_t sample_sizes[128], u_char *moof_pos, + uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, u_char *moof_pos, ngx_uint_t sample_rate) { u_char *pos; @@ -1040,8 +1031,7 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, ngx_rtmp_mp4_write_tfhd(b, sample_rate); ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time, sample_rate); - ngx_rtmp_mp4_write_trun(b, sample_count, sample_sizes, moof_pos, - sample_rate); + ngx_rtmp_mp4_write_trun(b, sample_count, samples, moof_pos); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1123,7 +1113,7 @@ ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, uint32_t sample_sizes[128], uint32_t index, + uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, uint32_t index, ngx_uint_t sample_rate) { u_char *pos; @@ -1136,7 +1126,7 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, b->last = ngx_cpymem(b->last, "moof", sizeof("moof")-1); ngx_rtmp_mp4_write_mfhd(b, index); - ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, sample_sizes, + ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, samples, pos, sample_rate); ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 7f67d02..233fa6f 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -9,6 +9,15 @@ #include +typedef struct { + uint32_t size; + uint32_t duration; + uint32_t delay; + uint32_t timestamp; + unsigned key:1; +} ngx_rtmp_mp4_sample_t; + + typedef struct { ngx_uint_t width; ngx_uint_t height; @@ -31,7 +40,7 @@ ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata); ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, uint32_t sample_sizes[128], uint32_t index, + uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, uint32_t index, ngx_uint_t sample_rate); ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t reference_size, ngx_uint_t earliest_pres_time, From c18fd3aad4aae5aedc26f00f703ea9bd813bf55c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 17:20:16 +0400 Subject: [PATCH 12/50] video & audio working --- dash/ngx_rtmp_dash_module.c | 75 +++++----- dash/ngx_rtmp_mp4.c | 274 +++++++++++++++++++++--------------- dash/ngx_rtmp_mp4.h | 18 ++- 3 files changed, 209 insertions(+), 158 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e8c1290..a1e4d5a 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -31,6 +31,7 @@ typedef struct { 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; @@ -170,7 +171,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_fd_t fd; ngx_str_t playlist, playlist_bak; ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_live_ctx_t *live_ctx; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_dash_app_conf_t *dacf; @@ -179,11 +179,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) 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); - live_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_live_module); - if (dacf == NULL || ctx == NULL || codec_ctx == NULL || - live_ctx == NULL || live_ctx->stream == NULL) - { + if (dacf == NULL || ctx == NULL || codec_ctx == NULL) { return NGX_ERROR; } @@ -228,9 +225,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " frameRate=\"%ui\"\n" \ " sar=\"1:1\"\n" \ " startWithSAP=\"1\"\n" \ - " bandwidth=\"%ui\">\n" \ + " bandwidth=\"0\">\n" \ " \n" \ + " bandwidth=\"0\">\n" \ " width, codec_ctx->height, codec_ctx->frame_rate, - (ngx_uint_t) (live_ctx->stream->bw_in.bandwidth * 8), - (ngx_uint_t) 0,//ctx->video.earliest_pres_time, (ngx_uint_t) dacf->fraglen, &ctx->name, &ctx->name); @@ -294,11 +289,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, - (ngx_uint_t) (ctx->audio.earliest_pres_time * - (float) codec_ctx->sample_rate / 1000), - codec_ctx->sample_rate, - (ngx_uint_t) (dacf->fraglen * codec_ctx->sample_rate - / 1000), + (ngx_uint_t) dacf->fraglen, &ctx->name, &ctx->name); n = ngx_write_fd(fd, buffer, p - buffer); @@ -372,6 +363,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) metadata.audio = 0; metadata.video = 1; + /*TODO: buffer control*/ ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); ngx_rtmp_mp4_write_moov(s, &b, &metadata); @@ -401,6 +393,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) metadata.video = 0; metadata.audio = 1; + /*TODO: buffer control*/ ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); ngx_rtmp_mp4_write_moov(s, &b, &metadata); @@ -417,8 +410,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) static void -ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, - ngx_uint_t sample_rate) +ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) { u_char *pos, *pos1; size_t left; @@ -451,13 +443,12 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, "dash: fragment earliest pts: %uD", t->earliest_pres_time); ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, - t->samples, t->id, sample_rate); + t->samples, t->sample_mask, t->id); pos1 = b.last; b.last = pos; - ngx_rtmp_mp4_write_sidx(s, &b, t->mdat_size + 8 + (pos1 - (pos + 44)), - t->earliest_pres_time, t->latest_pres_time, - sample_rate); + 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); @@ -518,21 +509,19 @@ done: static ngx_int_t ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) { - ngx_rtmp_dash_ctx_t *ctx; - ngx_rtmp_codec_ctx_t *codec_ctx; + ngx_rtmp_dash_ctx_t *ctx; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash: close fragments"); 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->opened) { return NGX_OK; } - ngx_rtmp_dash_close_fragment(s, &ctx->video, 0); - ngx_rtmp_dash_close_fragment(s, &ctx->audio, codec_ctx->sample_rate); + ngx_rtmp_dash_close_fragment(s, &ctx->video); + ngx_rtmp_dash_close_fragment(s, &ctx->audio); ngx_rtmp_dash_write_playlist(s); @@ -574,6 +563,16 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, 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; } @@ -774,7 +773,7 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, 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 boundary, uint32_t timestamp) + ngx_rtmp_dash_track_t *t, ngx_int_t key, uint32_t timestamp) { u_char *p; size_t size, bsize; @@ -798,23 +797,18 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, size += bsize; } - t->latest_pres_time = timestamp; - - if (t->sample_count > 0) { - t->samples[t->sample_count - 1].duration = timestamp - - t->samples[t->sample_count - 1].timestamp; - } - if (!ctx->opened) { ngx_rtmp_dash_open_fragments(s); } - ngx_rtmp_dash_update_fragments(s, boundary, timestamp); + 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) { @@ -827,7 +821,12 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, t->samples[t->sample_count].size = (uint32_t) size; t->samples[t->sample_count].duration = 0; t->samples[t->sample_count].timestamp = timestamp; - t->samples[t->sample_count].key = (boundary ? 1 : 0); + t->samples[t->sample_count].key = (key ? 1 : 0); + + if (t->sample_count > 0) { + t->samples[t->sample_count - 1].duration = timestamp - + t->samples[t->sample_count - 1].timestamp; + } t->sample_count++; t->mdat_size += (ngx_uint_t) size; diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 45d9184..6611302 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -178,7 +178,7 @@ ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -303,26 +303,37 @@ ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) static ngx_int_t -ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) +ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b) { - u_char *pos; + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "mdhd", sizeof("mdhd")-1); + b->last = ngx_cpymem(b->last, "mdhd", sizeof("mdhd") - 1); + + /* version */ + ngx_rtmp_mp4_field_32(b, 0); + + /* creation time */ + ngx_rtmp_mp4_field_32(b, 0); + + /* modification time */ + ngx_rtmp_mp4_field_32(b, 0); - ngx_rtmp_mp4_field_32(b, 0); /* version */ - ngx_rtmp_mp4_field_32(b, 0); /* creation time */ - ngx_rtmp_mp4_field_32(b, 0); /* modification time */ /* time scale*/ - ngx_rtmp_mp4_field_32(b, metadata->audio == 1 ? metadata->sample_rate : - NGX_RTMP_MP4_TIMESCALE); - ngx_rtmp_mp4_field_32(b, 0); /* duration */ - ngx_rtmp_mp4_field_16(b, 0x15C7); /* language */ - ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); + + /* duration */ + ngx_rtmp_mp4_field_32(b, 0); + + /* lanuguage */ + ngx_rtmp_mp4_field_16(b, 0x15C7); + + /* reserved */ + ngx_rtmp_mp4_field_16(b, 0); ngx_rtmp_mp4_update_box_size(b, pos); @@ -786,14 +797,14 @@ static ngx_int_t ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "minf", sizeof("minf")-1); + b->last = ngx_cpymem(b->last, "minf", sizeof("minf") - 1); if (metadata->video == 1) { ngx_rtmp_mp4_write_vmhd(b); @@ -815,16 +826,16 @@ static ngx_int_t ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "mdia", sizeof("mdia")-1); + b->last = ngx_cpymem(b->last, "mdia", sizeof("mdia") - 1); - ngx_rtmp_mp4_write_mdhd(b, metadata); + ngx_rtmp_mp4_write_mdhd(b); ngx_rtmp_mp4_write_hdlr(b, metadata); ngx_rtmp_mp4_write_minf(s, b, metadata); @@ -837,14 +848,14 @@ static ngx_int_t ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "trak", sizeof("trak")-1); + b->last = ngx_cpymem(b->last, "trak", sizeof("trak") - 1); ngx_rtmp_mp4_write_tkhd(b, metadata); ngx_rtmp_mp4_write_mdia(s, b, metadata); @@ -858,23 +869,14 @@ ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; - uint32_t sample_dur; - - if (metadata->video == 1) { - sample_dur = metadata->frame_rate > 0 ? NGX_RTMP_MP4_TIMESCALE / - metadata->frame_rate : NGX_RTMP_MP4_TIMESCALE; - } - else { - sample_dur = metadata->audio_codec == NGX_RTMP_AUDIO_AAC ? 1024 : 1152; - } + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "mvex", sizeof("mvex")-1); + b->last = ngx_cpymem(b->last, "mvex", sizeof("mvex") - 1); /* just write the trex and mehd in here too */ #if 0 @@ -885,17 +887,28 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ ngx_rtmp_mp4_field_32(b, 0x000D8D2A); /* frag duration */ #endif + ngx_rtmp_mp4_field_32(b, 0x20); - b->last = ngx_cpymem(b->last, "trex", sizeof("trex")-1); + b->last = ngx_cpymem(b->last, "trex", sizeof("trex") - 1); - ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ - ngx_rtmp_mp4_field_32(b, 1); /* track id */ - ngx_rtmp_mp4_field_32(b, 1); /* default sample description index */ - ngx_rtmp_mp4_field_32(b, sample_dur); /* default sample duration */ - ngx_rtmp_mp4_field_32(b, 0); /* default sample size */ - /* default sample flags */ - ngx_rtmp_mp4_field_32(b, metadata->audio == 1 ? 0 : 0x00010000); + /* version & flags */ + ngx_rtmp_mp4_field_32(b, 0); + + /* track id */ + ngx_rtmp_mp4_field_32(b, 1); + + /* default sample description index */ + ngx_rtmp_mp4_field_32(b, 1); + + /* default sample duration */ + ngx_rtmp_mp4_field_32(b, 0); + + /* default sample size, 1024 for AAC */ + ngx_rtmp_mp4_field_32(b, 1024); + + /* default sample flags, key on */ + ngx_rtmp_mp4_field_32(b, 0); ngx_rtmp_mp4_update_box_size(b, pos); @@ -927,7 +940,7 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t -ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b) { u_char *pos; @@ -939,11 +952,10 @@ ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) b->last = ngx_cpymem(b->last, "tfhd", sizeof("tfhd")-1); /* version & flags */ - ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00020020 : 0x00020000); - ngx_rtmp_mp4_field_32(b, 1); /* track id */ - if (sample_rate > 0) { - ngx_rtmp_mp4_field_32(b, 0x02000000); - } + ngx_rtmp_mp4_field_32(b, 0x00020000); + + /* track id */ + ngx_rtmp_mp4_field_32(b, 1); ngx_rtmp_mp4_update_box_size(b, pos); @@ -952,29 +964,20 @@ ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b, ngx_uint_t sample_rate) static ngx_int_t -ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, uint32_t earliest_pres_time) { - u_char *pos; - float multiplier; - - if (sample_rate > 0) { - multiplier = (float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE; - } - else { - multiplier = 1; - } + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "tfdt", sizeof("tfdt")-1); + b->last = ngx_cpymem(b->last, "tfdt", sizeof("tfdt") - 1); - ngx_rtmp_mp4_field_32(b, 0x00000000); /* version == 1 aka 64 bit integer */ - /* earliest presentation time */ - ngx_rtmp_mp4_field_32(b, (uint32_t)((float)earliest_pres_time*multiplier)); + /* version == 1 aka 64 bit integer */ + ngx_rtmp_mp4_field_32(b, 0x00000000); + ngx_rtmp_mp4_field_32(b, earliest_pres_time); ngx_rtmp_mp4_update_box_size(b, pos); @@ -984,29 +987,66 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_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, u_char *moof_pos) + ngx_rtmp_mp4_sample_t *samples, ngx_uint_t sample_mask, u_char *moof_pos) { u_char *pos; - uint32_t i, offset; + uint32_t i, offset, nitems, flags; pos = b->last; - offset = (pos - moof_pos) + 20 + (sample_count * 4 * 4) + 8; + nitems = 0; + + /* data offset present */ + flags = 0x01; + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_DURATION) { + nitems++; + flags |= 0x000100; + } + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_SIZE) { + nitems++; + flags |= 0x000200; + } + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_KEY) { + nitems++; + flags |= 0x000400; + } + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_DELAY) { + nitems++; + flags |= 0x000800; + } + + offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); b->last = ngx_cpymem(b->last, "trun", sizeof("trun") - 1); - ngx_rtmp_mp4_field_32(b, 0x00000f01); + ngx_rtmp_mp4_field_32(b, flags); ngx_rtmp_mp4_field_32(b, sample_count); ngx_rtmp_mp4_field_32(b, offset); - for (i = 0; i < sample_count; i++) { - ngx_rtmp_mp4_field_32(b, samples[i].duration); - ngx_rtmp_mp4_field_32(b, samples[i].size); - ngx_rtmp_mp4_field_32(b, samples[i].key ? 0x00000000 : 0x00010000); - ngx_rtmp_mp4_field_32(b, samples[i].delay); + for (i = 0; i < sample_count; i++, samples++) { + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_DURATION) { + ngx_rtmp_mp4_field_32(b, samples->duration); + } + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_SIZE) { + ngx_rtmp_mp4_field_32(b, samples->size); + } + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_KEY) { + ngx_rtmp_mp4_field_32(b, samples->key ? 0x00000000 : 0x00010000); + } + + if (sample_mask & NGX_RTMP_MP4_SAMPLE_DELAY) { + ngx_rtmp_mp4_field_32(b, samples->delay); + } } ngx_rtmp_mp4_update_box_size(b, pos); @@ -1016,22 +1056,22 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, static ngx_int_t -ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, u_char *moof_pos, - ngx_uint_t sample_rate) +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) { - u_char *pos; + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "traf", sizeof("traf")-1); + b->last = ngx_cpymem(b->last, "traf", sizeof("traf") - 1); - ngx_rtmp_mp4_write_tfhd(b, sample_rate); - ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time, sample_rate); - ngx_rtmp_mp4_write_trun(b, sample_count, samples, moof_pos); + 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_update_box_size(b, pos); @@ -1051,8 +1091,11 @@ ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) b->last = ngx_cpymem(b->last, "mfhd", sizeof("mfhd")-1); - ngx_rtmp_mp4_field_32(b, 0); /* don't know what this is */ - ngx_rtmp_mp4_field_32(b, index); /* fragment index. */ + /* don't know what this is */ + ngx_rtmp_mp4_field_32(b, 0); + + /* fragment index. */ + ngx_rtmp_mp4_field_32(b, index); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1061,49 +1104,52 @@ ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) ngx_int_t -ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_uint_t reference_size, ngx_uint_t earliest_pres_time, - ngx_uint_t latest_pres_time, ngx_uint_t sample_rate) +ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, + uint32_t earliest_pres_time, uint32_t latest_pres_time) { - u_char *pos; - uint32_t ept, dur; + u_char *pos; + uint32_t duration; - if (sample_rate > 0) { - ept = (uint32_t)((float)earliest_pres_time*((float)sample_rate / - (float)NGX_RTMP_MP4_TIMESCALE)); - dur = (uint32_t)((float)(latest_pres_time-earliest_pres_time) * - ((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); - } - else { - ept = earliest_pres_time; - dur = (latest_pres_time-earliest_pres_time); - } - - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: buffered dash range start: %uL, duration: %uL", - ept, dur); + duration = latest_pres_time - earliest_pres_time; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "sidx", sizeof("sidx")-1); + b->last = ngx_cpymem(b->last, "sidx", sizeof("sidx") - 1); + + /* version */ + ngx_rtmp_mp4_field_32(b, 0); + + /* reference id */ + ngx_rtmp_mp4_field_32(b, 1); - ngx_rtmp_mp4_field_32(b, 0); /* version */ - ngx_rtmp_mp4_field_32(b, 1); /* reference id */ /* timescale */ - ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? sample_rate : - NGX_RTMP_MP4_TIMESCALE); - ngx_rtmp_mp4_field_32(b, ept); /* earliest presentation time */ - ngx_rtmp_mp4_field_32(b, 0); /* first offset */ - ngx_rtmp_mp4_field_16(b, 0); /* reserved */ - ngx_rtmp_mp4_field_16(b, 1); /* reference count (=1) */ + ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); + + /* earliest presentation time */ + ngx_rtmp_mp4_field_32(b, earliest_pres_time); + + /* first offset */ + ngx_rtmp_mp4_field_32(b, duration); /*TODO*/ + + /* reserved */ + ngx_rtmp_mp4_field_16(b, 0); + + /* reference count = 1 */ + ngx_rtmp_mp4_field_16(b, 1); + /* 1st bit is reference type, the rest is reference size */ ngx_rtmp_mp4_field_32(b, reference_size); - ngx_rtmp_mp4_field_32(b, dur); /* subsegment duration */ + + /* subsegment duration */ + ngx_rtmp_mp4_field_32(b, duration); + /* first bit is startsWithSAP (=1), next 3 bits are SAP type (=001) */ ngx_rtmp_mp4_field_8(b, 0x90); - ngx_rtmp_mp4_field_24(b, 0); /* SAP delta time */ + + /* SAP delta time */ + ngx_rtmp_mp4_field_24(b, 0); ngx_rtmp_mp4_update_box_size(b, pos); @@ -1112,22 +1158,22 @@ ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_int_t -ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, uint32_t index, - ngx_uint_t sample_rate) +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) { - u_char *pos; + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "moof", sizeof("moof")-1); + b->last = ngx_cpymem(b->last, "moof", sizeof("moof") - 1); ngx_rtmp_mp4_write_mfhd(b, index); ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, samples, - pos, sample_rate); + sample_mask, pos); ngx_rtmp_mp4_update_box_size(b, pos); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index 233fa6f..dddc844 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -9,6 +9,12 @@ #include +#define NGX_RTMP_MP4_SAMPLE_SIZE 0x01 +#define NGX_RTMP_MP4_SAMPLE_DURATION 0x02 +#define NGX_RTMP_MP4_SAMPLE_DELAY 0x04 +#define NGX_RTMP_MP4_SAMPLE_KEY 0x08 + + typedef struct { uint32_t size; uint32_t duration; @@ -39,12 +45,12 @@ ngx_int_t ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_metadata_t *metadata); ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata); -ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, - uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, uint32_t index, - ngx_uint_t sample_rate); -ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_uint_t reference_size, ngx_uint_t earliest_pres_time, - ngx_uint_t latest_pres_time, ngx_uint_t sample_rate); +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_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); From 85d34da0d45e9f73c1fb959a14938885a4719778 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 17:38:47 +0400 Subject: [PATCH 13/50] style fixes --- dash/ngx_rtmp_mp4.c | 215 ++++++++++++++++++++++---------------------- 1 file changed, 107 insertions(+), 108 deletions(-) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 6611302..faa991a 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -6,35 +6,20 @@ #include -/* tie resolution */ -#define NGX_RTMP_MP4_TIMESCALE 1000 - -/* normal forward playback as defined by spec */ -#define NGX_RTMP_MP4_PREFERRED_RATE 0x00010000 - -/* full volume as defined by spec */ -#define NGX_RTMP_MP4_PREFERRED_VOLUME 0x0100 - - -static u_char compressor_name[] = { - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 -}; - - /* static ngx_int_t ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) { - u_char bytes[8]; + u_char bytes[8]; - bytes[0] = ((uint64_t)n >> 56) & 0xFF; - bytes[1] = ((uint64_t)n >> 48) & 0xFF; - bytes[2] = ((uint64_t)n >> 40) & 0xFF; - bytes[3] = ((uint64_t)n >> 32) & 0xFF; - bytes[4] = ((uint64_t)n >> 24) & 0xFF; - bytes[5] = ((uint64_t)n >> 16) & 0xFF; - bytes[6] = ((uint64_t)n >> 8) & 0xFF; - bytes[7] = (uint64_t)n & 0xFF; + bytes[0] = ((uint64_t) n >> 56) & 0xFF; + bytes[1] = ((uint64_t) n >> 48) & 0xFF; + bytes[2] = ((uint64_t) n >> 40) & 0xFF; + bytes[3] = ((uint64_t) n >> 32) & 0xFF; + bytes[4] = ((uint64_t) n >> 24) & 0xFF; + bytes[5] = ((uint64_t) n >> 16) & 0xFF; + bytes[6] = ((uint64_t) n >> 8) & 0xFF; + bytes[7] = (uint64_t) n & 0xFF; if (b->last + sizeof(bytes) > b->end) { return NGX_ERROR; @@ -49,12 +34,12 @@ ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) static ngx_int_t ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) { - u_char bytes[4]; + u_char bytes[4]; - bytes[0] = ((uint32_t)n >> 24) & 0xFF; - bytes[1] = ((uint32_t)n >> 16) & 0xFF; - bytes[2] = ((uint32_t)n >> 8) & 0xFF; - bytes[3] = (uint32_t)n & 0xFF; + bytes[0] = ((uint32_t) n >> 24) & 0xFF; + bytes[1] = ((uint32_t) n >> 16) & 0xFF; + bytes[2] = ((uint32_t) n >> 8) & 0xFF; + bytes[3] = (uint32_t) n & 0xFF; if (b->last + sizeof(bytes) > b->end) { return NGX_ERROR; @@ -69,11 +54,11 @@ ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) static ngx_int_t ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) { - u_char bytes[3]; + u_char bytes[3]; - bytes[0] = ((uint32_t)n >> 16) & 0xFF; - bytes[1] = ((uint32_t)n >> 8) & 0xFF; - bytes[2] = (uint32_t)n & 0xFF; + bytes[0] = ((uint32_t) n >> 16) & 0xFF; + bytes[1] = ((uint32_t) n >> 8) & 0xFF; + bytes[2] = (uint32_t) n & 0xFF; if (b->last + sizeof(bytes) > b->end) { return NGX_ERROR; @@ -88,10 +73,10 @@ ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) static ngx_int_t ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) { - u_char bytes[2]; + u_char bytes[2]; - bytes[0] = ((uint32_t)n >> 8) & 0xFF; - bytes[1] = (uint32_t)n & 0xFF; + bytes[0] = ((uint32_t) n >> 8) & 0xFF; + bytes[1] = (uint32_t) n & 0xFF; if (b->last + sizeof(bytes) > b->end) { return NGX_ERROR; @@ -106,7 +91,7 @@ ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) static ngx_int_t ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) { - u_char bytes[1]; + u_char bytes[1]; bytes[0] = n & 0xFF; @@ -121,15 +106,9 @@ ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) static ngx_int_t -ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { - //int i = 3; - - /* initially stolen from ffmpeg, but most of it isnt necessary */ - +ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) +{ ngx_rtmp_mp4_field_8(b, tag); - //for (; i > 0; i--) { - // ngx_rtmp_mp4_field_8(b, (size >> (7 * i)) | 0x80); - //} ngx_rtmp_mp4_field_8(b, size & 0x7F); return NGX_OK; @@ -139,12 +118,12 @@ ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { static ngx_int_t ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) { - u_char *curpos; + u_char *curpos; curpos = b->last; b->last = pos; - ngx_rtmp_mp4_field_32(b, (curpos-pos)); + ngx_rtmp_mp4_field_32(b, (uint32_t) (curpos - pos)); b->last = curpos; @@ -152,14 +131,18 @@ ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) } -/* transformation matrix - |a b u| - |c d v| - |tx ty w| */ static ngx_int_t ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, int16_t d, int16_t tx, int16_t ty) { + +/* + * transformation matrix + * |a b u| + * |c d v| + * |tx ty w| + */ + ngx_rtmp_mp4_field_32(buf, a << 16); /* 16.16 format */ ngx_rtmp_mp4_field_32(buf, b << 16); /* 16.16 format */ ngx_rtmp_mp4_field_32(buf, 0); /* u in 2.30 format */ @@ -222,7 +205,7 @@ ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, static ngx_int_t ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -234,10 +217,10 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 0x00000000); /* creation time */ ngx_rtmp_mp4_field_32(b, 0); /* modification time */ - ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); /* timescale */ + ngx_rtmp_mp4_field_32(b, 1000); /* timescale */ ngx_rtmp_mp4_field_32(b, 0); /* duration */ - ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_PREFERRED_RATE); /* playback rate */ - ngx_rtmp_mp4_field_16(b, NGX_RTMP_MP4_PREFERRED_VOLUME); /* volume rate */ + ngx_rtmp_mp4_field_32(b, 0x00010000); /* playback rate */ + ngx_rtmp_mp4_field_16(b, 0x0100); /* volume rate */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */ @@ -262,7 +245,7 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) static ngx_int_t ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -324,7 +307,7 @@ ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b) ngx_rtmp_mp4_field_32(b, 0); /* time scale*/ - ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); + ngx_rtmp_mp4_field_32(b, 1000); /* duration */ ngx_rtmp_mp4_field_32(b, 0); @@ -344,7 +327,7 @@ ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -418,7 +401,7 @@ ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -444,7 +427,7 @@ ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) static ngx_int_t ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -465,9 +448,9 @@ static ngx_int_t ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos, *p; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_chain_t *in; + u_char *pos, *p; + ngx_chain_t *in; + ngx_rtmp_codec_ctx_t *codec_ctx; codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); @@ -509,46 +492,63 @@ ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t *metadata) + ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; /* box size placeholder */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "avc1", sizeof("avc1")-1); + b->last = ngx_cpymem(b->last, "avc1", sizeof("avc1") - 1); - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_16(b, 0); /* reserved */ - ngx_rtmp_mp4_field_16(b, 1); /* data reference index */ - ngx_rtmp_mp4_field_16(b, 0); /* codec stream version */ - ngx_rtmp_mp4_field_16(b, 0); /* codec stream revision (=0) */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + /* 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, metadata->width); ngx_rtmp_mp4_field_16(b, metadata->height); - ngx_rtmp_mp4_field_32(b, 0x00480000); /* Horizontal resolution 72dpi */ - ngx_rtmp_mp4_field_32(b, 0x00480000); /* Vertical resolution 72dpi */ - ngx_rtmp_mp4_field_32(b, 0); /* Data size (= 0) */ - ngx_rtmp_mp4_field_16(b, 1); /* Frame count (= 1) */ - ngx_rtmp_mp4_field_8(b, 0); /* compressor name len */ - b->last = ngx_cpymem(b->last, compressor_name, sizeof(compressor_name)+1); + /* horizontal & vertical resolutions 72 dpi */ + ngx_rtmp_mp4_field_32(b, 0x00480000); + ngx_rtmp_mp4_field_32(b, 0x00480000); - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_16(b, 0x18); /* reserved */ - ngx_rtmp_mp4_field_16(b, 0xffff); /* reserved */ + /* 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, metadata); - ngx_rtmp_mp4_update_box_size(b, pos); return NGX_OK; @@ -557,14 +557,13 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t *metadata) + ngx_rtmp_mp4_metadata_t *metadata) { - - u_char *pos; - ngx_rtmp_codec_ctx_t *codec_ctx; - ngx_chain_t *aac; - int decoder_info; - int aac_header_offset; + int decoder_info; + int aac_header_offset; + u_char *pos; + ngx_chain_t *aac; + ngx_rtmp_codec_ctx_t *codec_ctx; codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); @@ -620,9 +619,9 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, - ngx_rtmp_mp4_metadata_t *metadata) + ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -656,9 +655,9 @@ ngx_rtmp_mp4_write_audio(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_metadata_t *metadata) + ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -686,7 +685,7 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_stts(ngx_buf_t *b) { - u_char *pos; + u_char *pos; pos = b->last; @@ -707,7 +706,7 @@ ngx_rtmp_mp4_write_stts(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) { - u_char *pos; + u_char *pos; pos = b->last; @@ -728,7 +727,7 @@ ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) { - u_char *pos; + u_char *pos; pos = b->last; @@ -750,7 +749,7 @@ ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) static ngx_int_t ngx_rtmp_mp4_write_stco(ngx_buf_t *b) { - u_char *pos; + u_char *pos; pos = b->last; @@ -772,7 +771,7 @@ static ngx_int_t ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -920,7 +919,7 @@ ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - u_char *pos; + u_char *pos; pos = b->last; @@ -942,7 +941,7 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, static ngx_int_t ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b) { - u_char *pos; + u_char *pos; pos = b->last; @@ -1125,7 +1124,7 @@ ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, ngx_rtmp_mp4_field_32(b, 1); /* timescale */ - ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); + ngx_rtmp_mp4_field_32(b, 1000); /* earliest presentation time */ ngx_rtmp_mp4_field_32(b, earliest_pres_time); @@ -1186,7 +1185,7 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) { ngx_rtmp_mp4_field_32(b, size); - b->last = ngx_cpymem(b->last, "mdat", sizeof("mdat")-1); + b->last = ngx_cpymem(b->last, "mdat", sizeof("mdat") - 1); return NGX_OK; } From 37033ed8c0fdd4a3b223cd821e8a5e500c59ca99 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 18:56:30 +0400 Subject: [PATCH 14/50] added bound checks & simplified box creation --- dash/ngx_rtmp_mp4.c | 380 ++++++++++++++++++-------------------------- 1 file changed, 152 insertions(+), 228 deletions(-) diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index faa991a..763679f 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -71,7 +71,7 @@ ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) static ngx_int_t -ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) +ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint16_t n) { u_char bytes[2]; @@ -89,7 +89,7 @@ ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) static ngx_int_t -ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) +ngx_rtmp_mp4_field_8(ngx_buf_t *b, uint8_t n) { u_char bytes[1]; @@ -116,14 +116,64 @@ ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) static ngx_int_t -ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) +ngx_rtmp_mp4_data(ngx_buf_t *b, void *data, size_t n) +{ + if (b->last + n > b->end) { + return NGX_ERROR; + } + + b->last = ngx_cpymem(b->last, (u_char *) data, n); + + return NGX_OK; +} + + +static ngx_int_t +ngx_rtmp_mp4_box(ngx_buf_t *b, const char box[4]) +{ + if (b->last + 4 > b->end) { + return NGX_ERROR; + } + + b->last = ngx_cpymem(b->last, (u_char *) box, 4); + + return NGX_OK; +} + + +static u_char * +ngx_rtmp_mp4_start_box(ngx_buf_t *b, const char box[4]) +{ + u_char *p; + + p = b->last; + + if (ngx_rtmp_mp4_field_32(b, 0) != NGX_OK) { + return NULL; + } + + if (ngx_rtmp_mp4_box(b, box) != NGX_OK) { + return NULL; + } + + return p; +} + + +static ngx_int_t +ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *p) { u_char *curpos; - curpos = b->last; - b->last = pos; + if (p == NULL) { + return NGX_ERROR; + } - ngx_rtmp_mp4_field_32(b, (uint32_t) (curpos - pos)); + curpos = b->last; + + b->last = p; + + ngx_rtmp_mp4_field_32(b, (uint32_t) (curpos - p)); b->last = curpos; @@ -163,35 +213,32 @@ ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - switch (type) { case NGX_RTMP_MP4_FILETYPE_INIT: - b->last = ngx_cpymem(b->last, "ftypiso5", sizeof("ftypiso5") - 1); + pos = ngx_rtmp_mp4_start_box(b, "ftyp"); + ngx_rtmp_mp4_box(b, "iso5"); ngx_rtmp_mp4_field_32(b, 1); if (metadata != NULL && metadata->video == 1) { - b->last = ngx_cpymem(b->last, "avc1iso5dash", - sizeof("avc1iso5dash") - 1); - } else { - b->last = ngx_cpymem(b->last, "iso5dash", sizeof("iso5dash") - 1); + ngx_rtmp_mp4_box(b, "avc1"); } + ngx_rtmp_mp4_box(b, "iso5"); + ngx_rtmp_mp4_box(b, "dash"); + break; - case NGX_RTMP_MP4_FILETYPE_SEG: + default: /* NGX_RTMP_MP4_FILETYPE_SEG */ - b->last = ngx_cpymem(b->last, "stypmsdh", sizeof("stypmsdh") - 1); + pos = ngx_rtmp_mp4_start_box(b, "styp"); + ngx_rtmp_mp4_box(b, "msdh"); ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "msdhmsix", sizeof("msdhmsix") - 1); + ngx_rtmp_mp4_box(b, "msdh"); + ngx_rtmp_mp4_box(b, "msix"); break; } @@ -207,12 +254,7 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "mvhd", sizeof("mvhd")-1); + pos = ngx_rtmp_mp4_start_box(b, "mvhd"); ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 0x00000000); /* creation time */ @@ -247,12 +289,7 @@ ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "tkhd", sizeof("tkhd")-1); + pos = ngx_rtmp_mp4_start_box(b, "tkhd"); ngx_rtmp_mp4_field_8(b, 0); /* version */ ngx_rtmp_mp4_field_24(b, 0x0000000F); /* flags */ @@ -290,12 +327,7 @@ ngx_rtmp_mp4_write_mdhd(ngx_buf_t *b) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "mdhd", sizeof("mdhd") - 1); + pos = ngx_rtmp_mp4_start_box(b, "mdhd"); /* version */ ngx_rtmp_mp4_field_32(b, 0); @@ -329,35 +361,32 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; - pos = b->last; + pos = ngx_rtmp_mp4_start_box(b, "hdlr"); - /* box size placeholder */ + /* version and flags */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "hdlr", sizeof("hdlr")-1); - - ngx_rtmp_mp4_field_32(b, 0); /* version and flags */ - ngx_rtmp_mp4_field_32(b, 0); /* pre defined (=0) */ - if (metadata->video == 1) { - /* video handler */ - b->last = ngx_cpymem(b->last, "vide", sizeof("vide")-1); - } - else { - /* sound handler */ - b->last = ngx_cpymem(b->last, "soun", sizeof("soun")-1); - } - - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved */ + /* pre defined */ + ngx_rtmp_mp4_field_32(b, 0); if (metadata->video == 1) { - /* video handler string--NULL TERMINATED */ - b->last = ngx_cpymem(b->last, "VideoHandler", sizeof("VideoHandler")); + ngx_rtmp_mp4_box(b, "vide"); + } else { + ngx_rtmp_mp4_box(b, "soun"); + } + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + + if (metadata->video == 1) { + /* video handler string, NULL-terminated */ + ngx_rtmp_mp4_data(b, "VideoHandler", sizeof("VideoHandler")); } else { - /* sound handler string--NULL TERMINATED */ - b->last = ngx_cpymem(b->last, "SoundHandler", sizeof("SoundHandler")); + /* sound handler string, NULL-terminated */ + ngx_rtmp_mp4_data(b, "SoundHandler", sizeof("SoundHandler")); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -369,14 +398,17 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) static ngx_int_t ngx_rtmp_mp4_write_vmhd(ngx_buf_t *b) { - /* size is always 0x14, apparently */ - ngx_rtmp_mp4_field_32(b, 0x14); + /* size is always 20, apparently */ + ngx_rtmp_mp4_field_32(b, 20); - b->last = ngx_cpymem(b->last, "vmhd", sizeof("vmhd")-1); + ngx_rtmp_mp4_box(b, "vmhd"); - ngx_rtmp_mp4_field_32(b, 0x01); /* version and flags */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved (graphics mode = copy) */ - ngx_rtmp_mp4_field_32(b, 0); /* reserved (graphics mode = copy) */ + /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0x01); + + /* reserved (graphics mode=copy) */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); return NGX_OK; } @@ -388,11 +420,14 @@ ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) /* size is always 16, apparently */ ngx_rtmp_mp4_field_32(b, 16); - b->last = ngx_cpymem(b->last, "smhd", sizeof("smhd")-1); + ngx_rtmp_mp4_box(b, "smhd"); - ngx_rtmp_mp4_field_32(b, 0); /* version and flags */ - ngx_rtmp_mp4_field_16(b, 0); /* reserved (balance, normally = 0) */ - ngx_rtmp_mp4_field_16(b, 0); /* reserved */ + /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0); + + /* reserved (balance normally=0) */ + ngx_rtmp_mp4_field_16(b, 0); + ngx_rtmp_mp4_field_16(b, 0); return NGX_OK; } @@ -403,20 +438,21 @@ ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; - pos = b->last; + pos = ngx_rtmp_mp4_start_box(b, "dref"); - /* box size placeholder */ + /* version and flags */ ngx_rtmp_mp4_field_32(b, 0); - b->last = ngx_cpymem(b->last, "dref", sizeof("dref")-1); + /* entry count */ + ngx_rtmp_mp4_field_32(b, 1); - ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ - ngx_rtmp_mp4_field_32(b, 1); /* entry count */ - ngx_rtmp_mp4_field_32(b, 0xc); /* size of url */ + /* url size */ + ngx_rtmp_mp4_field_32(b, 0xc); - b->last = ngx_cpymem(b->last, "url ", sizeof("url ")-1); + ngx_rtmp_mp4_box(b, "url "); - ngx_rtmp_mp4_field_32(b, 0x00000001); /* version & flags */ + /* version and flags */ + ngx_rtmp_mp4_field_32(b, 0x00000001); ngx_rtmp_mp4_update_box_size(b, pos); @@ -429,12 +465,7 @@ ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "dinf", sizeof("dinf")-1); + pos = ngx_rtmp_mp4_start_box(b, "dinf"); ngx_rtmp_mp4_write_dref(b, metadata); @@ -463,25 +494,22 @@ ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, return NGX_ERROR; } - pos = b->last; + pos = ngx_rtmp_mp4_start_box(b, "avcC"); - for (p=in->buf->pos;p<=in->buf->last;p++) { - if (*p == 0x01) { /* check for start code */ + /* assume config fits ont chunk (highly probable) */ + + /* check for start code */ + for (p = in->buf->pos; p <= in->buf->last; p++) { + if (*p == 0x01) { break; } } - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "avcC", sizeof("avcC")-1); - - if (in->buf->last-p > 0) { - b->last = ngx_cpymem(b->last, p, in->buf->last-p); - } - else { + if (in->buf->last - p > 0) { + ngx_rtmp_mp4_data(b, p, (size_t) (in->buf->last - p)); + } else { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "hls: dash: invalid avcc received"); + "dash: invalid avcc received"); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -496,12 +524,7 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "avc1", sizeof("avc1") - 1); + pos = ngx_rtmp_mp4_start_box(b, "avc1"); /* reserved */ ngx_rtmp_mp4_field_32(b, 0); @@ -575,17 +598,12 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, if (aac == NULL) { decoder_info = 0; aac_header_offset = 0; - } - else { + } else { decoder_info = (aac->buf->last-aac->buf->pos); aac_header_offset = 2; } - pos = b->last; - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "esds", sizeof("esds")-1); + pos = ngx_rtmp_mp4_start_box(b, "esds"); ngx_rtmp_mp4_field_32(b, 0); /* version */ /* length of the rest of the box */ @@ -604,8 +622,8 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, if (aac) { ngx_rtmp_mp4_put_descr(b, 0x05, decoder_info); - b->last = ngx_cpymem(b->last, aac->buf->pos+aac_header_offset, - decoder_info); + ngx_rtmp_mp4_data(b, aac->buf->pos + aac_header_offset, + (size_t) decoder_info); } ngx_rtmp_mp4_put_descr(b, 0x06, 1); @@ -623,12 +641,7 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "mp4a", sizeof("mp4a")-1); + pos = ngx_rtmp_mp4_start_box(b, "mp4a"); ngx_rtmp_mp4_field_32(b, 0); /* reserved */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */ @@ -659,12 +672,7 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "stsd", sizeof("stsd")-1); + pos = ngx_rtmp_mp4_start_box(b, "stsd"); ngx_rtmp_mp4_field_32(b, 0); /* version & flags */ ngx_rtmp_mp4_field_32(b, 1); /* entry count */ @@ -687,12 +695,7 @@ ngx_rtmp_mp4_write_stts(ngx_buf_t *b) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "stts", sizeof("stts")-1); + pos = ngx_rtmp_mp4_start_box(b, "stts"); ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 0); /* entry count */ @@ -708,12 +711,7 @@ ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "stsc", sizeof("stsc")-1); + pos = ngx_rtmp_mp4_start_box(b, "stsc"); ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 0); /* entry count */ @@ -729,12 +727,7 @@ ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "stsz", sizeof("stsz")-1); + pos = ngx_rtmp_mp4_start_box(b, "stsz"); ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 0); /* entry count */ @@ -751,12 +744,7 @@ ngx_rtmp_mp4_write_stco(ngx_buf_t *b) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "stco", sizeof("stco")-1); + pos = ngx_rtmp_mp4_start_box(b, "stco"); ngx_rtmp_mp4_field_32(b, 0); /* version */ ngx_rtmp_mp4_field_32(b, 0); /* entry count */ @@ -773,12 +761,7 @@ ngx_rtmp_mp4_write_stbl(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "stbl", sizeof("stbl")-1); + pos = ngx_rtmp_mp4_start_box(b, "stbl"); ngx_rtmp_mp4_write_stsd(s, b, metadata); ngx_rtmp_mp4_write_stts(b); @@ -798,12 +781,7 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "minf", sizeof("minf") - 1); + pos = ngx_rtmp_mp4_start_box(b, "minf"); if (metadata->video == 1) { ngx_rtmp_mp4_write_vmhd(b); @@ -827,12 +805,7 @@ ngx_rtmp_mp4_write_mdia(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "mdia", sizeof("mdia") - 1); + pos = ngx_rtmp_mp4_start_box(b, "mdia"); ngx_rtmp_mp4_write_mdhd(b); ngx_rtmp_mp4_write_hdlr(b, metadata); @@ -849,12 +822,7 @@ ngx_rtmp_mp4_write_trak(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "trak", sizeof("trak") - 1); + pos = ngx_rtmp_mp4_start_box(b, "trak"); ngx_rtmp_mp4_write_tkhd(b, metadata); ngx_rtmp_mp4_write_mdia(s, b, metadata); @@ -870,12 +838,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "mvex", sizeof("mvex") - 1); + pos = ngx_rtmp_mp4_start_box(b, "mvex"); /* just write the trex and mehd in here too */ #if 0 @@ -889,7 +852,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) ngx_rtmp_mp4_field_32(b, 0x20); - b->last = ngx_cpymem(b->last, "trex", sizeof("trex") - 1); + ngx_rtmp_mp4_box(b, "trex"); /* version & flags */ ngx_rtmp_mp4_field_32(b, 0); @@ -921,12 +884,7 @@ ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "moov", sizeof("moov")-1); + pos = ngx_rtmp_mp4_start_box(b, "moov"); ngx_rtmp_mp4_write_mvhd(b, metadata); ngx_rtmp_mp4_write_mvex(b, metadata); @@ -943,12 +901,7 @@ ngx_rtmp_mp4_write_tfhd(ngx_buf_t *b) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "tfhd", sizeof("tfhd")-1); + pos = ngx_rtmp_mp4_start_box(b, "tfhd"); /* version & flags */ ngx_rtmp_mp4_field_32(b, 0x00020000); @@ -967,12 +920,7 @@ ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, uint32_t earliest_pres_time) { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "tfdt", sizeof("tfdt") - 1); + pos = ngx_rtmp_mp4_start_box(b, "tfdt"); /* version == 1 aka 64 bit integer */ ngx_rtmp_mp4_field_32(b, 0x00000000); @@ -991,7 +939,7 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, u_char *pos; uint32_t i, offset, nitems, flags; - pos = b->last; + pos = ngx_rtmp_mp4_start_box(b, "trun"); nitems = 0; @@ -1020,11 +968,6 @@ ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, offset = (pos - moof_pos) + 20 + (sample_count * nitems * 4) + 8; - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "trun", sizeof("trun") - 1); - ngx_rtmp_mp4_field_32(b, flags); ngx_rtmp_mp4_field_32(b, sample_count); ngx_rtmp_mp4_field_32(b, offset); @@ -1061,12 +1004,7 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "traf", sizeof("traf") - 1); + pos = ngx_rtmp_mp4_start_box(b, "traf"); ngx_rtmp_mp4_write_tfhd(b); ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time); @@ -1081,14 +1019,9 @@ ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time, static ngx_int_t ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) { - u_char *pos; + u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "mfhd", sizeof("mfhd")-1); + pos = ngx_rtmp_mp4_start_box(b, "mfhd"); /* don't know what this is */ ngx_rtmp_mp4_field_32(b, 0); @@ -1110,12 +1043,8 @@ ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size, uint32_t duration; duration = latest_pres_time - earliest_pres_time; - pos = b->last; - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "sidx", sizeof("sidx") - 1); + pos = ngx_rtmp_mp4_start_box(b, "sidx"); /* version */ ngx_rtmp_mp4_field_32(b, 0); @@ -1163,12 +1092,7 @@ ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time, { u_char *pos; - pos = b->last; - - /* box size placeholder */ - ngx_rtmp_mp4_field_32(b, 0); - - b->last = ngx_cpymem(b->last, "moof", sizeof("moof") - 1); + 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, @@ -1185,7 +1109,7 @@ ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) { ngx_rtmp_mp4_field_32(b, size); - b->last = ngx_cpymem(b->last, "mdat", sizeof("mdat") - 1); + ngx_rtmp_mp4_box(b, "mdat"); return NGX_OK; } From fe4877f2168e7e62d6a45cb26708d662f0416961 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 19:48:49 +0400 Subject: [PATCH 15/50] implemented timeline dash --- dash/ngx_rtmp_dash_module.c | 90 +++++++++++++++++++++++++------------ 1 file changed, 62 insertions(+), 28 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index a1e4d5a..64d26ac 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -166,10 +166,11 @@ ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) static ngx_int_t ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { - u_char *p; + u_char *p, *last; ssize_t n; ngx_fd_t fd; ngx_str_t playlist, playlist_bak; + ngx_uint_t i; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_dash_app_conf_t *dacf; @@ -204,7 +205,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " type=\"dynamic\"\n" \ " xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" \ " availabilityStartTime=\"%V\"\n" \ - " minBufferTime=\"PT3S\"\n" \ + " minimumUpdatePeriod=\"PT%uiS\"\n" \ + " minBufferTime=\"PT1S\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n" \ " \n" @@ -229,14 +231,22 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ + " media=\"%V-$Time$.m4v\"\n" \ + " initialization=\"%V-init.m4v\">\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" \ @@ -254,10 +264,14 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ + " media=\"%V-$Time$.m4a\"\n" \ + " initialization=\"%V-init.m4a\">\n" \ + " \n" + + +#define NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER \ + " \n" \ + " \n" \ " \n" \ " \n" @@ -266,36 +280,54 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ "\n" - p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER, - &ctx->start_time); + last = buffer + sizeof(buffer); + + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, + &ctx->start_time, (ngx_uint_t) (dacf->fraglen / 1000)); n = ngx_write_fd(fd, buffer, p - buffer); if (ctx->has_video) { - p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_VIDEO, + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_VIDEO, codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, - (ngx_uint_t) dacf->fraglen, &ctx->name, &ctx->name); + + for (i = 0; i <= ctx->id; i++) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + (uint32_t) dacf->fraglen * i, + (uint32_t) dacf->fraglen); + } + + 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_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_AUDIO, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, - (ngx_uint_t) dacf->fraglen, &ctx->name, &ctx->name); + + for (i = 0; i <= ctx->id; i++) { + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_TIME, + (uint32_t) dacf->fraglen * i, + (uint32_t) dacf->fraglen); + } + + p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER); + n = ngx_write_fd(fd, buffer, p - buffer); } - p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_FOOTER); + p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_FOOTER); n = ngx_write_fd(fd, buffer, p - buffer); if (n < 0) { @@ -412,14 +444,15 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) 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; + 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_app_conf_t *dacf; - static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; + static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; if (!t->opened) { return; @@ -428,6 +461,7 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash: close fragment id=%ui, type=%c", t->id, t->type); + dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); b.start = buffer; @@ -454,8 +488,8 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) /* move the data down to make room for the headers */ - *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%ui.m4%c", - t->id, t->type) = 0; + *ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uD.m4%c", + (uint32_t) (t->id * dacf->fraglen), t->type) = 0; fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -672,8 +706,8 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ctx->stream.len = p - ctx->playlist.data + 1; ctx->stream.data = ngx_palloc(s->connection->pool, - ctx->stream.len + NGX_INT_T_LEN + - sizeof("xxxx.m4x")); + 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] = '-'; From df33838e37170c7709421d5efc2899698e0ba36e Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 20:42:29 +0400 Subject: [PATCH 16/50] reimplemented fragments --- dash/ngx_rtmp_dash_module.c | 106 +++++++++++++++++++++++++++++------- 1 file changed, 85 insertions(+), 21 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 64d26ac..8ec78e6 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -26,6 +26,12 @@ static ngx_int_t ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s); #define NGX_RTMP_DASH_MAX_SAMPLES 1024 +typedef struct { + uint32_t timestamp; + uint32_t duration; +} ngx_rtmp_dash_frag_t; + + typedef struct { ngx_uint_t id; ngx_uint_t opened; @@ -47,6 +53,10 @@ typedef struct { ngx_str_t stream; ngx_str_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; @@ -150,6 +160,36 @@ ngx_module_t ngx_rtmp_dash_module = { }; +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) { @@ -173,6 +213,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_uint_t i; 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]; @@ -297,10 +338,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) &ctx->name, &ctx->name); - for (i = 0; i <= ctx->id; i++) { + 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, - (uint32_t) dacf->fraglen * i, - (uint32_t) dacf->fraglen); + f->timestamp, f->duration); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_VIDEO_FOOTER); @@ -316,10 +357,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) &ctx->name, &ctx->name); - for (i = 0; i <= ctx->id; i++) { + 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, - (uint32_t) dacf->fraglen * i, - (uint32_t) dacf->fraglen); + f->timestamp, f->duration); } p = ngx_slprintf(p, last, NGX_RTMP_DASH_MANIFEST_AUDIO_FOOTER); @@ -450,7 +491,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]; @@ -461,7 +502,6 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "dash: close fragment id=%ui, type=%c", t->id, t->type); - dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); b.start = buffer; @@ -488,8 +528,10 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) /* 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", - (uint32_t) (t->id * dacf->fraglen), t->type) = 0; + f->timestamp, t->type) = 0; fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); @@ -557,6 +599,8 @@ ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) 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++; @@ -647,6 +691,7 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) u_char *p; size_t len; 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); @@ -670,6 +715,23 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) } 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; @@ -773,14 +835,14 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, { 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); - /*TODO: non-zero initial timestamp */ - - hit = (timestamp >= (ctx->id + 1) * dacf->fraglen); + hit = (timestamp >= f->timestamp + dacf->fraglen); if (ctx->has_video && !hit) { boundary = 0; @@ -798,9 +860,18 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, boundary = 1; } + if (!ctx->opened) { + boundary = 1; + } + + f->duration = (timestamp - f->timestamp); + 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; } } @@ -809,14 +880,11 @@ 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) { - u_char *p; - size_t size, bsize; - ngx_rtmp_dash_ctx_t *ctx; + u_char *p; + size_t size, bsize; static u_char buffer[NGX_RTMP_DASH_BUFSIZE]; - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - p = buffer; size = 0; @@ -831,10 +899,6 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, size += bsize; } - if (!ctx->opened) { - ngx_rtmp_dash_open_fragments(s); - } - ngx_rtmp_dash_update_fragments(s, key, timestamp); if (t->sample_count == 0) { From 3b680915a8747d7e4d44db86bba5ebf7282a07d7 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 20:53:54 +0400 Subject: [PATCH 17/50] implemented live presentation offset --- dash/ngx_rtmp_dash_module.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 8ec78e6..e40f60f 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -208,6 +208,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { u_char *p, *last; ssize_t n; + uint32_t time_offset; ngx_fd_t fd; ngx_str_t playlist, playlist_bak; ngx_uint_t i; @@ -270,7 +271,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " startWithSAP=\"1\"\n" \ " bandwidth=\"0\">\n" \ " \n" \ @@ -303,7 +304,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " startWithSAP=\"1\"\n" \ " bandwidth=\"0\">\n" \ " \n" \ @@ -321,6 +322,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ "\n" + f = ngx_rtmp_dash_get_frag(s, 0); + time_offset = f->timestamp; + last = buffer + sizeof(buffer); p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, @@ -335,6 +339,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, + time_offset, &ctx->name, &ctx->name); @@ -354,6 +359,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, + time_offset, &ctx->name, &ctx->name); From 2c7111cf103ea06b7cd0ac3e4214b905cc667646 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 21:34:10 +0400 Subject: [PATCH 18/50] fixed opening fragments & improved style --- dash/ngx_rtmp_dash_module.c | 28 +++++++++++----------------- dash/ngx_rtmp_mp4.c | 2 +- dash/ngx_rtmp_mp4.h | 26 +++++++++++++------------- 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index e40f60f..607cf83 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -505,8 +505,9 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) return; } - ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: close fragment id=%ui, type=%c", t->id, t->type); + 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); @@ -519,9 +520,6 @@ 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_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: fragment earliest pts: %uD", t->earliest_pres_time); - ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, t->samples, t->sample_mask, t->id); pos1 = b.last; @@ -593,15 +591,14 @@ ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) { ngx_rtmp_dash_ctx_t *ctx; - ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "dash: close fragments"); - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); - if (!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); @@ -626,6 +623,9 @@ ngx_rtmp_dash_open_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, 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; @@ -675,15 +675,9 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) return NGX_OK; } - if (ctx->has_video) { - ngx_rtmp_dash_open_fragment(s, &ctx->video, ctx->id, 'v'); - } + ngx_rtmp_dash_open_fragment(s, &ctx->video, ctx->id, 'v'); - if (ctx->has_audio) { - ngx_rtmp_dash_open_fragment(s, &ctx->audio, ctx->id, 'a'); - } - - //ctx->id++; + ngx_rtmp_dash_open_fragment(s, &ctx->audio, ctx->id, 'a'); ctx->opened = 1; diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 763679f..7e9b51b 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -496,7 +496,7 @@ ngx_rtmp_mp4_write_avcc(ngx_rtmp_session_t *s, ngx_buf_t *b, pos = ngx_rtmp_mp4_start_box(b, "avcC"); - /* assume config fits ont chunk (highly probable) */ + /* assume config fits one chunk (highly probable) */ /* check for start code */ for (p = in->buf->pos; p <= in->buf->last; p++) { diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index dddc844..efcfcd6 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -16,28 +16,28 @@ typedef struct { - uint32_t size; - uint32_t duration; - uint32_t delay; - uint32_t timestamp; - unsigned key:1; + uint32_t size; + uint32_t duration; + uint32_t delay; + uint32_t timestamp; + unsigned key:1; } ngx_rtmp_mp4_sample_t; typedef struct { - ngx_uint_t width; - ngx_uint_t height; - ngx_uint_t audio; - ngx_uint_t video; - ngx_uint_t sample_rate; - ngx_uint_t frame_rate; - ngx_uint_t audio_codec; + ngx_uint_t width; + ngx_uint_t height; + ngx_uint_t audio; + ngx_uint_t video; + ngx_uint_t sample_rate; + ngx_uint_t frame_rate; + ngx_uint_t audio_codec; } ngx_rtmp_mp4_metadata_t; enum { NGX_RTMP_MP4_FILETYPE_INIT = 0, - NGX_RTMP_MP4_FILETYPE_SEG = 1 + NGX_RTMP_MP4_FILETYPE_SEG }; From 8c0b83514c9f0cb745ef575356c9d416e0e87ace Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 21:45:54 +0400 Subject: [PATCH 19/50] added buffering based on fraglen --- dash/ngx_rtmp_dash_module.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 607cf83..8b4daae 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -248,7 +248,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" \ " availabilityStartTime=\"%V\"\n" \ " minimumUpdatePeriod=\"PT%uiS\"\n" \ - " minBufferTime=\"PT1S\"\n" \ + " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n" \ " \n" @@ -328,7 +328,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) last = buffer + sizeof(buffer); p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, - &ctx->start_time, (ngx_uint_t) (dacf->fraglen / 1000)); + &ctx->start_time, + (ngx_uint_t) (dacf->fraglen / 1000), + (ngx_uint_t) (dacf->fraglen / 500)); + n = ngx_write_fd(fd, buffer, p - buffer); if (ctx->has_video) { From 0dca45f0b67f5ce6511a6eb6f1b98fd3b55c4d01 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 22:00:07 +0400 Subject: [PATCH 20/50] fixed dash fragment cleanup --- dash/ngx_rtmp_dash_module.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 8b4daae..744c89a 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1143,28 +1143,28 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) name.data[name.len - 2] == '4' && name.data[name.len - 1] == 'v') { - max_age = playlen / 166; + 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 / 166; + 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 / 166; + 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 / 166; + max_age = playlen / 1000; } else { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, From a9ddfed095e1fc4200b8c9bd0916816031c45d88 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 12 Nov 2013 22:22:50 +0400 Subject: [PATCH 21/50] reduced buffering time --- 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 744c89a..750d005 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -330,7 +330,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, &ctx->start_time, (ngx_uint_t) (dacf->fraglen / 1000), - (ngx_uint_t) (dacf->fraglen / 500)); + (ngx_uint_t) (dacf->fraglen / 1000)); n = ngx_write_fd(fd, buffer, p - buffer); From bec6d8553acc74df8e987d9b80d273d95f0ca935 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Wed, 20 Nov 2013 21:19:30 +0400 Subject: [PATCH 22/50] preserve dash init files from cleanup --- dash/ngx_rtmp_dash_module.c | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 750d005..ae6771e 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1138,10 +1138,21 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) continue; } - 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') + if (name.len >= 9 && name.data[name.len - 9] == '-' && + 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') + { + continue; + + } 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; From 81ce4788f91b1f46c6ee655c9e8844cdb8b18172 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 21 Nov 2013 03:13:16 +0400 Subject: [PATCH 23/50] optimized for dash.js live --- dash/ngx_rtmp_dash_module.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index ae6771e..9660683 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -247,9 +247,11 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " type=\"dynamic\"\n" \ " xmlns=\"urn:mpeg:dash:schema:mpd:2011\"\n" \ " availabilityStartTime=\"%V\"\n" \ + " availabilityEndTime=\"%V\"\n" \ " minimumUpdatePeriod=\"PT%uiS\"\n" \ " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ + " suggestedPresentationDelay=\"0\"\n" \ " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n" \ " \n" @@ -328,6 +330,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) last = buffer + sizeof(buffer); p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, + &ctx->start_time, &ctx->start_time, (ngx_uint_t) (dacf->fraglen / 1000), (ngx_uint_t) (dacf->fraglen / 1000)); From 85dbf397e412700ed42dc29474cdf2930fd441af Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 21 Nov 2013 11:39:04 +0400 Subject: [PATCH 24/50] added bound checks & simplified box creation --- dash/ngx_rtmp_dash_module.c | 43 +++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 9660683..29329c2 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -208,8 +208,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { u_char *p, *last; ssize_t n; - uint32_t time_offset; + uint32_t dur; ngx_fd_t fd; + ngx_tm_t tm; ngx_str_t playlist, playlist_bak; ngx_uint_t i; ngx_rtmp_dash_ctx_t *ctx; @@ -218,6 +219,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) 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")]; dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); @@ -246,8 +249,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) "\n" \ " \n" \ @@ -306,7 +309,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " startWithSAP=\"1\"\n" \ " bandwidth=\"0\">\n" \ " \n" \ @@ -324,14 +327,34 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ "\n" - f = ngx_rtmp_dash_get_frag(s, 0); - time_offset = f->timestamp; + dur = ngx_rtmp_dash_get_frag(s, ctx->nfrags)->timestamp - + ngx_rtmp_dash_get_frag(s, 0)->timestamp; + + ngx_localtime(ngx_cached_time->sec, &tm); + + *ngx_sprintf(start_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", + tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec, + ngx_cached_time->gmtoff < 0 ? '-' : '+', + ngx_abs(ngx_cached_time->gmtoff / 60), + ngx_abs(ngx_cached_time->gmtoff % 60)) = 0; + + ngx_localtime(ngx_cached_time->sec + dur / 1000, &tm); + + *ngx_sprintf(start_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", + tm.ngx_tm_year, tm.ngx_tm_mon, + tm.ngx_tm_mday, tm.ngx_tm_hour, + tm.ngx_tm_min, tm.ngx_tm_sec, + ngx_cached_time->gmtoff < 0 ? '-' : '+', + ngx_abs(ngx_cached_time->gmtoff / 60), + ngx_abs(ngx_cached_time->gmtoff % 60)) = 0; last = buffer + sizeof(buffer); p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_HEADER, - &ctx->start_time, - &ctx->start_time, + start_time, + end_time, (ngx_uint_t) (dacf->fraglen / 1000), (ngx_uint_t) (dacf->fraglen / 1000)); @@ -345,7 +368,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, - time_offset, &ctx->name, &ctx->name); @@ -365,7 +387,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, - time_offset, &ctx->name, &ctx->name); From 24addede157097a1f521575c6b610ad72b46bf9d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 21 Nov 2013 11:55:07 +0400 Subject: [PATCH 25/50] improved dash timing --- dash/ngx_rtmp_dash_module.c | 35 +++++++++++++++-------------------- 1 file changed, 15 insertions(+), 20 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 29329c2..6cb7ee9 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -51,7 +51,7 @@ typedef struct { ngx_str_t playlist_bak; ngx_str_t name; ngx_str_t stream; - ngx_str_t start_time; + ngx_time_t start_time; ngx_uint_t nfrags; ngx_uint_t frag; @@ -208,7 +208,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) { u_char *p, *last; ssize_t n; - uint32_t dur; ngx_fd_t fd; ngx_tm_t tm; ngx_str_t playlist, playlist_bak; @@ -327,28 +326,29 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ "\n" - dur = ngx_rtmp_dash_get_frag(s, ctx->nfrags)->timestamp - - ngx_rtmp_dash_get_frag(s, 0)->timestamp; - - ngx_localtime(ngx_cached_time->sec, &tm); + ngx_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.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec, - ngx_cached_time->gmtoff < 0 ? '-' : '+', - ngx_abs(ngx_cached_time->gmtoff / 60), - ngx_abs(ngx_cached_time->gmtoff % 60)) = 0; + ctx->start_time.gmtoff < 0 ? '-' : '+', + ngx_abs(ctx->start_time.gmtoff / 60), + ngx_abs(ctx->start_time.gmtoff % 60)) = 0; - ngx_localtime(ngx_cached_time->sec + dur / 1000, &tm); + ngx_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(start_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", + *ngx_sprintf(end_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", tm.ngx_tm_year, tm.ngx_tm_mon, tm.ngx_tm_mday, tm.ngx_tm_hour, tm.ngx_tm_min, tm.ngx_tm_sec, - ngx_cached_time->gmtoff < 0 ? '-' : '+', - ngx_abs(ngx_cached_time->gmtoff / 60), - ngx_abs(ngx_cached_time->gmtoff % 60)) = 0; + ctx->start_time.gmtoff < 0 ? '-' : '+', + ngx_abs(ctx->start_time.gmtoff / 60), + ngx_abs(ctx->start_time.gmtoff % 60)) = 0; last = buffer + sizeof(buffer); @@ -820,12 +820,7 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) "dash: playlist='%V' playlist_bak='%V' stream_pattern='%V'", &ctx->playlist, &ctx->playlist_bak, &ctx->stream); - /* start time for mpd */ - ctx->start_time.data = ngx_palloc(s->connection->pool, - ngx_cached_http_log_iso8601.len); - ngx_memcpy(ctx->start_time.data, ngx_cached_http_log_iso8601.data, - ngx_cached_http_log_iso8601.len); - ctx->start_time.len = ngx_cached_http_log_iso8601.len; + ctx->start_time = *ngx_cached_time; next: return next_publish(s, v); From f6af3f2343e9f192da20131ee8b8a3acb97dcbe7 Mon Sep 17 00:00:00 2001 From: azure provisioned user Date: Sat, 23 Nov 2013 10:34:10 +0000 Subject: [PATCH 26/50] fixed MPD Validation issue --- 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 6cb7ee9..9bacc32 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -253,7 +253,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " minimumUpdatePeriod=\"PT%uiS\"\n" \ " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ - " suggestedPresentationDelay=\"0\"\n" \ + " suggestedPresentationDelay=\"PT0S\"\n" \ " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n" \ " \n" From 884366a2e066bf13928cf91687f8133ffc78e3c8 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Sat, 23 Nov 2013 15:16:04 +0400 Subject: [PATCH 27/50] fixed times --- dash/ngx_rtmp_dash_module.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 6cb7ee9..939e959 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -253,7 +253,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " minimumUpdatePeriod=\"PT%uiS\"\n" \ " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ - " suggestedPresentationDelay=\"0\"\n" \ + " suggestedPresentationDelay=\"PT%uiS\"\n" \ " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n" \ " \n" @@ -356,7 +356,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) start_time, end_time, (ngx_uint_t) (dacf->fraglen / 1000), - (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); From 9227aca90d92e7d6ebd8cb5a5e64ef461111254a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Sat, 23 Nov 2013 13:42:45 +0000 Subject: [PATCH 28/50] fixed times --- dash/ngx_rtmp_dash_module.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 9bacc32..5c6db53 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -253,7 +253,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " minimumUpdatePeriod=\"PT%uiS\"\n" \ " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ - " suggestedPresentationDelay=\"PT0S\"\n" \ + " suggestedPresentationDelay=\"PT%uiS\"\n" \ " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n" \ " \n" @@ -356,7 +356,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) start_time, end_time, (ngx_uint_t) (dacf->fraglen / 1000), - (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); From a95065b90db1c8d4e48b456f1cd03002e3a5e96c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Sat, 23 Nov 2013 16:19:41 +0000 Subject: [PATCH 29/50] fixed bandwith representation --- dash/ngx_rtmp_dash_module.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 5c6db53..8c86a8e 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -273,7 +273,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " frameRate=\"%ui\"\n" \ " sar=\"1:1\"\n" \ " startWithSAP=\"1\"\n" \ - " bandwidth=\"0\">\n" \ + " bandwidth=\"%ui\">\n" \ " \n" \ + " bandwidth=\"%ui\">\n" \ " width, codec_ctx->height, - codec_ctx->frame_rate, + codec_ctx->frame_rate, codec_ctx->width, codec_ctx->height, - codec_ctx->frame_rate, + codec_ctx->frame_rate, + (ngx_uint_t) (codec_ctx->video_data_rate * 1000), &ctx->name, &ctx->name); @@ -388,6 +389,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, + (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), &ctx->name, &ctx->name); From 33240c33dfd2b7288f3aa8b0d726a3ba3b0777ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Mon, 25 Nov 2013 09:59:04 +0000 Subject: [PATCH 30/50] fixed representation id naming --- dash/ngx_rtmp_dash_module.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 8c86a8e..ab1b701 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -265,7 +265,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " maxHeight=\"%ui\"\n" \ " maxFrameRate=\"%ui\">\n" \ " \n" \ " width, codec_ctx->height, codec_ctx->frame_rate, + &ctx->name, codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, @@ -385,7 +386,8 @@ 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_AUDIO, + &ctx->name, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, From ba94a0c1e45585a293897490e1fe0bfb40520e85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Mon, 25 Nov 2013 11:01:07 +0000 Subject: [PATCH 31/50] adding some HbbTV compliancy to MPD --- dash/ngx_rtmp_dash_module.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index ab1b701..2332572 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -253,8 +253,11 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " minimumUpdatePeriod=\"PT%uiS\"\n" \ " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ - " suggestedPresentationDelay=\"PT%uiS\"\n" \ - " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\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" \ " \n" From 15ec66a7bf07b6a777083a70a5cb0c9a3baa467c Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Tue, 26 Nov 2013 17:13:38 +0400 Subject: [PATCH 32/50] added presentation delay to dash fragments --- dash/ngx_rtmp_dash_module.c | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 84da966..07926fc 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -903,7 +903,7 @@ ngx_rtmp_dash_update_fragments(ngx_rtmp_session_t *s, ngx_int_t boundary, 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) + ngx_rtmp_dash_track_t *t, ngx_int_t key, uint32_t timestamp, uint32_t delay) { u_char *p; size_t size, bsize; @@ -1003,7 +1003,7 @@ ngx_rtmp_dash_audio(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, in->buf->pos += 2; - return ngx_rtmp_dash_append(s, in, &ctx->audio, 0, h->timestamp); + return ngx_rtmp_dash_append(s, in, &ctx->audio, 0, h->timestamp, 0); } @@ -1011,7 +1011,9 @@ 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; @@ -1045,13 +1047,21 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, 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); + return ngx_rtmp_dash_append(s, in, &ctx->video, ftype == 1, h->timestamp, + delay); } From ed68e4a2eb75be82fba1d3844f63d5a6f3d6ef2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Wed, 27 Nov 2013 17:04:09 +0000 Subject: [PATCH 33/50] added presentation delay to dash fragments + Repr. IDs --- dash/ngx_rtmp_dash_module.c | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 2332572..844dbfa 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -263,6 +263,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) #define NGX_RTMP_DASH_MANIFEST_VIDEO \ " \n" \ " buf->pos += 2; - return ngx_rtmp_dash_append(s, in, &ctx->audio, 0, h->timestamp); + return ngx_rtmp_dash_append(s, in, &ctx->audio, 0, h->timestamp, 0); } @@ -1016,7 +1018,9 @@ 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; @@ -1049,6 +1053,14 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, 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; @@ -1056,7 +1068,8 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, in->buf->pos += 5; - return ngx_rtmp_dash_append(s, in, &ctx->video, ftype == 1, h->timestamp); + return ngx_rtmp_dash_append(s, in, &ctx->video, ftype == 1, h->timestamp, + delay); } From b73075e28cefa2b96261d36a1616f609125e4809 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 14:14:40 +0400 Subject: [PATCH 34/50] added dash_nested, added directory creation, fixed init frags cleanup --- dash/ngx_rtmp_dash_module.c | 205 +++++++++++++++++++++++++++++++----- hls/ngx_rtmp_hls_module.c | 2 +- 2 files changed, 180 insertions(+), 27 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 07926fc..d1346a6 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -24,6 +24,7 @@ static ngx_int_t ngx_rtmp_dash_write_init_segments(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 typedef struct { @@ -81,6 +82,7 @@ typedef struct { ngx_flag_t dash; ngx_msec_t fraglen; ngx_msec_t playlen; + ngx_flag_t nested; ngx_str_t path; ngx_uint_t winfrags; ngx_flag_t cleanup; @@ -125,6 +127,13 @@ static ngx_command_t ngx_rtmp_dash_commands[] = { 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_null_command }; @@ -206,11 +215,12 @@ ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) 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; ngx_tm_t tm; - ngx_str_t playlist, playlist_bak; + ngx_str_t playlist, playlist_bak, noname, *name; ngx_uint_t i; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -277,8 +287,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ + " media=\"%V%s$Time$.m4v\"\n" \ + " initialization=\"%V%sinit.m4v\">\n" \ " \n" @@ -310,8 +320,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ + " media=\"%V%s$Time$.m4a\"\n" \ + " initialization=\"%V%sinit.m4a\">\n" \ " \n" @@ -361,6 +371,11 @@ ngx_rtmp_dash_write_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 ? "" : "-"); + if (ctx->has_video) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_VIDEO, codec_ctx->width, @@ -369,9 +384,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, - (ngx_uint_t) (codec_ctx->video_data_rate * 1000), - &ctx->name, - &ctx->name); + (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); @@ -390,8 +405,8 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) "40.2" : "6b", codec_ctx->sample_rate, (ngx_uint_t) (codec_ctx->audio_data_rate * 1000), - &ctx->name, - &ctx->name); + name, sep, + name, sep); for (i = 0; i < ctx->nfrags; i++) { f = ngx_rtmp_dash_get_frag(s, i); @@ -715,6 +730,103 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) } +static ngx_int_t +ngx_rtmp_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); + + if (ngx_file_info(dacf->path.data, &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(dacf->path.data, 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) { @@ -782,6 +894,9 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) *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); @@ -792,19 +907,28 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) p = ngx_cpymem(p, ctx->name.data, ctx->name.len); - /* ctx->playlist holds initial part of stream file path + /* + * ctx->stream holds initial part of stream file path * however the space for the whole stream path - * is allocated */ + * 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] = '-'; - p = ngx_cpymem(p, ".mpd", sizeof(".mpd") - 1); + ngx_memcpy(ctx->stream.data, ctx->playlist.data, ctx->stream.len - 1); + ctx->stream.data[ctx->stream.len - 1] = (dacf->nested ? '/' : '-'); + + 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 */ @@ -825,6 +949,10 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ctx->start_time = *ngx_cached_time; + if (ngx_rtmp_ensure_directory(s) != NGX_OK) { + return NGX_ERROR; + } + next: return next_publish(s, v); } @@ -1084,13 +1212,14 @@ ngx_rtmp_dash_stream_eof(ngx_rtmp_session_t *s, ngx_rtmp_stream_eof_t *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]; - ngx_dir_t dir; - ngx_err_t err; - ngx_str_t name, spath; - ngx_int_t nentries, nerased; + 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); @@ -1170,8 +1299,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) continue; } - if (name.len >= 9 && name.data[name.len - 9] == '-' && - name.data[name.len - 8] == 'i' && + 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' && @@ -1179,7 +1307,29 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) name.data[name.len - 3] == 'm' && name.data[name.len - 2] == '4') { - continue; + 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' && @@ -1226,7 +1376,8 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) if (ngx_delete_file(spath.data) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "dash: cleanup error '%V'", &spath); + "dash: cleanup " ngx_delete_file_n " failed on '%V'", + &spath); continue; } @@ -1260,6 +1411,7 @@ ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) conf->fraglen = NGX_CONF_UNSET_MSEC; conf->playlen = NGX_CONF_UNSET_MSEC; conf->cleanup = NGX_CONF_UNSET; + conf->nested = NGX_CONF_UNSET; return conf; } @@ -1277,6 +1429,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_str_value(conf->path, prev->path, ""); ngx_conf_merge_value(conf->cleanup, prev->cleanup, 1); + ngx_conf_merge_value(conf->nested, prev->nested, 0); if (conf->fraglen) { conf->winfrags = conf->playlen / conf->fraglen; diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index cf3d5ec..10bda54 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -1142,7 +1142,7 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) p = ngx_cpymem(p, ctx->name.data, ctx->name.len); - /* ctx->stream_path holds initial part of stream file path + /* ctx->stream holds initial part of stream file path * however the space for the whole stream path * is allocated */ From 29dfda882aa06bbb20278999b8b9ad48a1d348b7 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 17:14:39 +0400 Subject: [PATCH 35/50] fixed using raw non-null-terminated strings from config parser in syscalls --- dash/ngx_rtmp_dash_module.c | 4 +++- hls/ngx_rtmp_hls_module.c | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index d1346a6..3bc81e5 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -742,7 +742,9 @@ ngx_rtmp_ensure_directory(ngx_rtmp_session_t *s) dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module); - if (ngx_file_info(dacf->path.data, &fi) == NGX_FILE_ERROR) { + *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, diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 10bda54..3b5319a 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -313,7 +313,9 @@ ngx_rtmp_hls_create_parent_dir(ngx_rtmp_session_t *s) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "hls: creating target folder: '%V'", &hacf->path); - if (ngx_create_dir(hacf->path.data, NGX_RTMP_HLS_DIR_ACCESS) != NGX_OK) { + *ngx_snprintf(path, sizeof(path) - 1, "%V", &hacf->path) = 0; + + if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) != NGX_OK) { err = ngx_errno; if (err != NGX_EEXIST) { ngx_log_error(NGX_LOG_ERR, s->connection->log, err, From fcbd18c7f746e2e030c919ac5f5addf97aeae0b4 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 17:26:06 +0400 Subject: [PATCH 36/50] fixed windows compilation --- dash/ngx_rtmp_dash_module.c | 34 +++++++++++++++++++++------------- dash/ngx_rtmp_mp4.c | 12 ++++++------ 2 files changed, 27 insertions(+), 19 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 3bc81e5..fe850a4 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -219,7 +219,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) u_char *p, *last; ssize_t n; ngx_fd_t fd; - ngx_tm_t tm; + struct tm tm; ngx_str_t playlist, playlist_bak, noname, *name; ngx_uint_t i; ngx_rtmp_dash_ctx_t *ctx; @@ -336,26 +336,26 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " \n" \ "\n" - ngx_localtime(ctx->start_time.sec + - ngx_rtmp_dash_get_frag(s, 0)->timestamp / 1000, &tm); + 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.ngx_tm_year, tm.ngx_tm_mon, - tm.ngx_tm_mday, tm.ngx_tm_hour, - tm.ngx_tm_min, tm.ngx_tm_sec, + tm.tm_year, tm.tm_mon, + 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_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_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.ngx_tm_year, tm.ngx_tm_mon, - tm.ngx_tm_mday, tm.ngx_tm_hour, - tm.ngx_tm_min, tm.ngx_tm_sec, + tm.tm_year, tm.tm_mon, + 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; @@ -597,11 +597,19 @@ ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t) 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) { diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 7e9b51b..547f752 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -108,7 +108,7 @@ ngx_rtmp_mp4_field_8(ngx_buf_t *b, uint8_t n) static ngx_int_t ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { - ngx_rtmp_mp4_field_8(b, tag); + ngx_rtmp_mp4_field_8(b, (uint8_t) tag); ngx_rtmp_mp4_field_8(b, size & 0x7F); return NGX_OK; @@ -182,8 +182,8 @@ ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *p) static ngx_int_t -ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, - int16_t d, int16_t tx, int16_t ty) +ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, uint32_t a, uint32_t b, uint32_t c, + uint32_t d, uint32_t tx, uint32_t ty) { /* @@ -543,8 +543,8 @@ ngx_rtmp_mp4_write_video(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_32(b, 0); /* width & height */ - ngx_rtmp_mp4_field_16(b, metadata->width); - ngx_rtmp_mp4_field_16(b, metadata->height); + ngx_rtmp_mp4_field_16(b, (uint16_t) metadata->width); + ngx_rtmp_mp4_field_16(b, (uint16_t) metadata->height); /* horizontal & vertical resolutions 72 dpi */ ngx_rtmp_mp4_field_32(b, 0x00480000); @@ -653,7 +653,7 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_field_16(b, 16); /* something mp4 specific */ ngx_rtmp_mp4_field_16(b, 0); /* something mp4 specific */ ngx_rtmp_mp4_field_16(b, 0); /* packet size (=0) */ - ngx_rtmp_mp4_field_16(b, metadata->sample_rate); /* sample rate */ + ngx_rtmp_mp4_field_16(b, (uint16_t) metadata->sample_rate); ngx_rtmp_mp4_field_16(b, 0); /* reserved */ ngx_rtmp_mp4_write_esds(s, b, metadata); From 9abf8355d95eae8ebd795f356f0eef83e2e96c63 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 18:21:08 +0400 Subject: [PATCH 37/50] removed old variable --- 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 fe850a4..f151ea1 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -220,7 +220,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ssize_t n; ngx_fd_t fd; struct tm tm; - ngx_str_t playlist, playlist_bak, noname, *name; + ngx_str_t noname, *name; ngx_uint_t i; ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_codec_ctx_t *codec_ctx; @@ -434,7 +434,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data)) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "dash: rename failed: '%V'->'%V'", - &playlist_bak, &playlist); + &ctx->playlist_bak, &ctx->playlist); return NGX_ERROR; } From 749cea7cb7757aa6f774c633def7dd7c621d07ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Mon, 25 Nov 2013 09:59:04 +0000 Subject: [PATCH 38/50] fixed representation id naming --- dash/ngx_rtmp_dash_module.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index f151ea1..90d7ed6 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -275,7 +275,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " maxHeight=\"%ui\"\n" \ " maxFrameRate=\"%ui\">\n" \ " \n" \ " width, codec_ctx->height, codec_ctx->frame_rate, + &ctx->name, codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, @@ -400,7 +401,8 @@ 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_AUDIO, + &ctx->name, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, From a1b1c205f190168a7bd9beff2c290650b541810d Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 18:55:38 +0400 Subject: [PATCH 39/50] removed extra ngx_de_info call in win32 --- dash/ngx_rtmp_dash_module.c | 2 +- hls/ngx_rtmp_hls_module.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index f151ea1..931ce30 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1280,7 +1280,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) nentries++; - if (ngx_de_info(path, &dir) == NGX_FILE_ERROR) { + 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); diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 3b5319a..09e2765 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -1941,7 +1941,7 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) nentries++; - if (ngx_de_info(path, &dir) == NGX_FILE_ERROR) { + if (!dir.valid_info && ngx_de_info(path, &dir) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, "hls: cleanup " ngx_de_info_n " \"%V\" failed", &spath); From fb3adeda9f0440eb5539d5963815875e58ebcc35 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Mon, 25 Nov 2013 11:01:07 +0000 Subject: [PATCH 40/50] adding some HbbTV compliancy to MPD --- 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 90d7ed6..ad9a7bb 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -264,7 +264,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) " minBufferTime=\"PT%uiS\"\n" \ " timeShiftBufferDepth=\"PT0H0M0.00S\"\n" \ " suggestedPresentationDelay=\"PT%uiS\"\n" \ - " profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\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" \ " \n" From 4b3385562ab9c909ff7fd87f9d86a8aaa6e43e11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Calles?= Date: Wed, 27 Nov 2013 17:04:09 +0000 Subject: [PATCH 41/50] added presentation delay to dash fragments + Repr. IDs --- dash/ngx_rtmp_dash_module.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index ad9a7bb..ac886fc 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -273,6 +273,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) #define NGX_RTMP_DASH_MANIFEST_VIDEO \ " \n" \ " buf->pos[4]; + p[1] = in->buf->pos[3]; + p[2] = in->buf->pos[2]; + p[3] = 0; + p = (u_char *) &delay; From ec5687f45972bbf09ab3bbd0d310ac13c99a347a Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 19:05:21 +0400 Subject: [PATCH 42/50] improved result code check to work on windows --- dash/ngx_rtmp_dash_module.c | 7 ++++--- hls/ngx_rtmp_hls_module.c | 8 +++++--- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 931ce30..aeae90a 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1294,9 +1294,10 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "dash: cleanup dir '%V'", &name); - if (ngx_delete_dir(spath.data) != NGX_OK) { + if (ngx_delete_dir(spath.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "dash: cleanup dir error '%V'", &spath); + "dash: cleanup " ngx_delete_dir_n + "failed on '%V'", &spath); } else { nerased++; } @@ -1384,7 +1385,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) "dash: cleanup '%V' mtime=%T age=%T", &name, mtime, ngx_cached_time->sec - mtime); - if (ngx_delete_file(spath.data) != NGX_OK) { + if (ngx_delete_file(spath.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, "dash: cleanup " ngx_delete_file_n " failed on '%V'", &spath); diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 09e2765..a32054e 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -1957,7 +1957,8 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) if (ngx_delete_dir(spath.data) != NGX_OK) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "hls: cleanup dir error '%V'", &spath); + "hls: cleanup " ngx_delete_dir_n + "failed on '%V'", &spath); } else { nerased++; } @@ -1999,9 +2000,10 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) "hls: cleanup '%V' mtime=%T age=%T", &name, mtime, ngx_cached_time->sec - mtime); - if (ngx_delete_file(spath.data) != NGX_OK) { + if (ngx_delete_file(spath.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, - "hls: cleanup error '%V'", &spath); + "hls: cleanup " ngx_delete_file_n " failed on '%V'", + &spath); continue; } From c928742cb98c97e72b51ed11eef7b47a857607a8 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 19:11:31 +0400 Subject: [PATCH 43/50] fixed file op result code checks --- dash/ngx_rtmp_dash_module.c | 4 +++- hls/ngx_rtmp_hls_module.c | 16 ++++++++-------- 2 files changed, 11 insertions(+), 9 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index aeae90a..f7bea34 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -431,7 +431,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) ngx_close_file(fd); - if (ngx_rtmp_dash_rename_file(ctx->playlist_bak.data, ctx->playlist.data)) { + 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); diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index a32054e..dc23f7f 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -474,10 +474,10 @@ ngx_rtmp_hls_write_variant_playlist(ngx_rtmp_session_t *s) ngx_close_file(fd); - rc = ngx_rtmp_hls_rename_file(ctx->var_playlist_bak.data, - ctx->var_playlist.data); - - if (rc != NGX_OK) { + if (ngx_rtmp_hls_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, "hls: rename failed: '%V'->'%V'", &ctx->var_playlist_bak, &ctx->var_playlist); @@ -497,7 +497,7 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_hls_ctx_t *ctx; ssize_t n; ngx_rtmp_hls_app_conf_t *hacf; - ngx_int_t nretry, rc; + ngx_int_t nretry; ngx_rtmp_hls_frag_t *f; ngx_uint_t i, max_frag; ngx_str_t name_part; @@ -592,9 +592,9 @@ retry: ngx_close_file(fd); - rc = ngx_rtmp_hls_rename_file(ctx->playlist_bak.data, ctx->playlist.data); - - if (rc != NGX_OK) { + if (ngx_rtmp_hls_rename_file(ctx->playlist_bak.data, ctx->playlist.data) + == NGX_FILE_ERROR) + { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "hls: rename failed: '%V'->'%V'", &ctx->playlist_bak, &ctx->playlist); From 43718388f4e3b09cfcdcf9a72b4f0a4bac619ab4 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 19:51:15 +0400 Subject: [PATCH 44/50] fixed time formatting & file op error code check --- dash/ngx_rtmp_dash_module.c | 4 ++-- hls/ngx_rtmp_hls_module.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index f7bea34..2fd7805 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -340,7 +340,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) 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, tm.tm_mon, + 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 ? '-' : '+', @@ -353,7 +353,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) 1000, &tm); *ngx_sprintf(end_time, "%4d-%02d-%02dT%02d:%02d:%02d%c%02d:%02d", - tm.tm_year, tm.tm_mon, + 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 ? '-' : '+', diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index dc23f7f..09d80ac 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -315,7 +315,7 @@ ngx_rtmp_hls_create_parent_dir(ngx_rtmp_session_t *s) *ngx_snprintf(path, sizeof(path) - 1, "%V", &hacf->path) = 0; - if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) != NGX_OK) { + if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) == NGX_FILE_ERROR) { err = ngx_errno; if (err != NGX_EEXIST) { ngx_log_error(NGX_LOG_ERR, s->connection->log, err, @@ -342,7 +342,7 @@ ngx_rtmp_hls_create_parent_dir(ngx_rtmp_session_t *s) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "hls: creating nested folder: '%s'", path); - if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) != NGX_OK) { + if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "hls: error creating nested folder: '%s'", path); return NGX_ERROR; From d11823bcf8f04497a69011ad1aeb133fff0345f7 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 21:06:20 +0400 Subject: [PATCH 45/50] directory creation in hls module is now performed at publish stage --- dash/ngx_rtmp_dash_module.c | 8 +- hls/ngx_rtmp_hls_module.c | 191 ++++++++++++++++++++---------------- 2 files changed, 108 insertions(+), 91 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 2fd7805..472039d 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -741,7 +741,7 @@ ngx_rtmp_dash_open_fragments(ngx_rtmp_session_t *s) static ngx_int_t -ngx_rtmp_ensure_directory(ngx_rtmp_session_t *s) +ngx_rtmp_dash_ensure_directory(ngx_rtmp_session_t *s) { size_t len; ngx_file_info_t fi; @@ -765,9 +765,7 @@ ngx_rtmp_ensure_directory(ngx_rtmp_session_t *s) /* ENOENT */ - if (ngx_create_dir(dacf->path.data, NGX_RTMP_DASH_DIR_ACCESS) - == NGX_FILE_ERROR) - { + 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); @@ -961,7 +959,7 @@ ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ctx->start_time = *ngx_cached_time; - if (ngx_rtmp_ensure_directory(s) != NGX_OK) { + if (ngx_rtmp_dash_ensure_directory(s) != NGX_OK) { return NGX_ERROR; } diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 09d80ac..f1dacce 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -295,63 +295,6 @@ ngx_module_t ngx_rtmp_hls_module = { }; -static ngx_int_t -ngx_rtmp_hls_create_parent_dir(ngx_rtmp_session_t *s) -{ - ngx_rtmp_hls_app_conf_t *hacf; - ngx_rtmp_hls_ctx_t *ctx; - ngx_err_t err; - size_t len; - static u_char path[NGX_MAX_PATH + 1]; - - hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module); - - if (hacf->path.len == 0) { - return NGX_ERROR; - } - - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "hls: creating target folder: '%V'", &hacf->path); - - *ngx_snprintf(path, sizeof(path) - 1, "%V", &hacf->path) = 0; - - if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) == NGX_FILE_ERROR) { - err = ngx_errno; - if (err != NGX_EEXIST) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, err, - "hls: error creating target folder: '%V'", - &hacf->path); - return NGX_ERROR; - } - } - - if (!hacf->nested) { - return NGX_OK; - } - - ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); - - len = hacf->path.len; - if (hacf->path.data[len - 1] == '/') { - len--; - } - - *ngx_snprintf(path, sizeof(path) - 1, "%*s/%V", len, hacf->path.data, - &ctx->name) = 0; - - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, - "hls: creating nested folder: '%s'", path); - - if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, - "hls: error creating nested folder: '%s'", path); - return NGX_ERROR; - } - - return NGX_OK; -} - - static ngx_rtmp_hls_frag_t * ngx_rtmp_hls_get_frag(ngx_rtmp_session_t *s, ngx_int_t n) { @@ -497,7 +440,6 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s) ngx_rtmp_hls_ctx_t *ctx; ssize_t n; ngx_rtmp_hls_app_conf_t *hacf; - ngx_int_t nretry; ngx_rtmp_hls_frag_t *f; ngx_uint_t i, max_frag; ngx_str_t name_part; @@ -507,26 +449,13 @@ ngx_rtmp_hls_write_playlist(ngx_rtmp_session_t *s) hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); - nretry = 0; - -retry: - fd = ngx_open_file(ctx->playlist_bak.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (fd == NGX_INVALID_FILE) { - - if (ngx_errno == NGX_ENOENT && nretry == 0 && - ngx_rtmp_hls_create_parent_dir(s) == NGX_OK) - { - nretry++; - goto retry; - } - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "hls: " ngx_open_file_n " failed: '%V'", &ctx->playlist_bak); - return NGX_ERROR; } @@ -844,7 +773,6 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts, { ngx_rtmp_hls_ctx_t *ctx; ngx_rtmp_hls_frag_t *f; - ngx_uint_t nretry; uint64_t id; ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); @@ -867,22 +795,10 @@ ngx_rtmp_hls_open_fragment(ngx_rtmp_session_t *s, uint64_t ts, ngx_str_set(&ctx->file.name, "hls"); - nretry = 0; - -retry: - ctx->file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_WRONLY, NGX_FILE_TRUNCATE, NGX_FILE_DEFAULT_ACCESS); if (ctx->file.fd == NGX_INVALID_FILE) { - - if (ngx_errno == NGX_ENOENT && nretry == 0 && - ngx_rtmp_hls_create_parent_dir(s) == NGX_OK) - { - nretry++; - goto retry; - } - ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, "hls: error creating fragment file"); return NGX_ERROR; @@ -1059,6 +975,103 @@ done: } +static ngx_int_t +ngx_rtmp_hls_ensure_directory(ngx_rtmp_session_t *s) +{ + size_t len; + ngx_file_info_t fi; + ngx_rtmp_hls_ctx_t *ctx; + ngx_rtmp_hls_app_conf_t *hacf; + + static u_char path[NGX_MAX_PATH + 1]; + + hacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_hls_module); + + *ngx_snprintf(path, sizeof(path) - 1, "%V", &hacf->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, + "hls: " ngx_file_info_n " failed on '%V'", + &hacf->path); + return NGX_ERROR; + } + + /* ENOENT */ + + if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "hls: " ngx_create_dir_n " failed on '%V'", + &hacf->path); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "hls: directory '%V' created", &hacf->path); + + } else { + + if (!ngx_is_dir(&fi)) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "hls: '%V' exists and is not a directory", + &hacf->path); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "hls: directory '%V' exists", &hacf->path); + } + + if (!hacf->nested) { + return NGX_OK; + } + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); + + len = hacf->path.len; + if (hacf->path.data[len - 1] == '/') { + len--; + } + + *ngx_snprintf(path, sizeof(path) - 1, "%*s/%V", len, hacf->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, + "hls: directory '%s' exists", path); + return NGX_OK; + } + + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "hls: '%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, + "hls: " ngx_file_info_n " failed on '%s'", path); + return NGX_ERROR; + } + + /* NGX_ENOENT */ + + if (ngx_create_dir(path, NGX_RTMP_HLS_DIR_ACCESS) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, + "hls: " ngx_create_dir_n " failed on '%s'", path); + return NGX_ERROR; + } + + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "hls: directory '%s' created", path); + + return NGX_OK; +} + + static ngx_int_t ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) { @@ -1144,9 +1157,11 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) p = ngx_cpymem(p, ctx->name.data, ctx->name.len); - /* ctx->stream holds initial part of stream file path + /* + * ctx->stream holds initial part of stream file path * however the space for the whole stream path - * is allocated */ + * is allocated + */ ctx->stream.len = p - ctx->playlist.data + 1; ctx->stream.data = ngx_palloc(s->connection->pool, @@ -1230,6 +1245,10 @@ ngx_rtmp_hls_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) ngx_rtmp_hls_restore_stream(s); } + if (ngx_rtmp_hls_ensure_directory(s) != NGX_OK) { + return NGX_ERROR; + } + next: return next_publish(s, v); } From 0eb32ae1ca0b80d236df5c2b81ce37885f155187 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 21:31:30 +0400 Subject: [PATCH 46/50] fixing hls & dash cleanup --- dash/ngx_rtmp_dash_module.c | 6 +++--- hls/ngx_rtmp_hls_module.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 472039d..0ad7552 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1294,10 +1294,10 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "dash: cleanup dir '%V'", &name); - if (ngx_delete_dir(spath.data) == NGX_FILE_ERROR) { + 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); + " failed on '%V'", &spath); } else { nerased++; } @@ -1385,7 +1385,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) "dash: cleanup '%V' mtime=%T age=%T", &name, mtime, ngx_cached_time->sec - mtime); - if (ngx_delete_file(spath.data) == NGX_FILE_ERROR) { + 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); diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index f1dacce..d9b75a0 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -1974,10 +1974,10 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup dir '%V'", &name); - if (ngx_delete_dir(spath.data) != NGX_OK) { + if (ngx_delete_dir(path) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, "hls: cleanup " ngx_delete_dir_n - "failed on '%V'", &spath); + " failed on '%V' ('%s')", &spath, path); } else { nerased++; } @@ -2019,7 +2019,7 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) "hls: cleanup '%V' mtime=%T age=%T", &name, mtime, ngx_cached_time->sec - mtime); - if (ngx_delete_file(spath.data) == NGX_FILE_ERROR) { + if (ngx_delete_file(path) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, "hls: cleanup " ngx_delete_file_n " failed on '%V'", &spath); From 43ececd070424e7e3bef86c5564f387af67aa402 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 21:35:59 +0400 Subject: [PATCH 47/50] test debug logging --- hls/ngx_rtmp_hls_module.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index d9b75a0..334e46e 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -1959,6 +1959,7 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) spath.len = p - path; nentries++; + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 1 '%s'", path); if (!dir.valid_info && ngx_de_info(path, &dir) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, @@ -1967,12 +1968,15 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) continue; } + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 2 '%s'", path); if (ngx_de_is_dir(&dir)) { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 3 '%s'", path); if (ngx_rtmp_hls_cleanup_dir(&spath, playlen) == 0) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup dir '%V'", &name); + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 4 '%s'", path); if (ngx_delete_dir(path) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, From ed09425a5d286f5c7cabb411fb5f1e232de7c9df Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 21:40:19 +0400 Subject: [PATCH 48/50] test debug logging --- dash/ngx_rtmp_dash_module.c | 2 +- hls/ngx_rtmp_hls_module.c | 9 ++++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 0ad7552..6585fbc 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1261,7 +1261,7 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, err, "dash: cleanup " ngx_read_dir_n - " \"%V\" failed", ppath); + " '%V' failed", ppath); return NGX_ERROR; } diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 334e46e..9bdba95 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -1914,11 +1914,13 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) "hls: cleanup path='%V' playlen=%M", ppath, playlen); + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 1 '%s'", ppath->data); if (ngx_open_dir(ppath, &dir) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, ngx_errno, "hls: cleanup open dir failed '%V'", ppath); return NGX_ERROR; } + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 2 '%s'", ppath->data); nentries = 0; nerased = 0; @@ -1941,7 +1943,7 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, err, "hls: cleanup " ngx_read_dir_n - " \"%V\" failed", ppath); + " '%V' failed", ppath); return NGX_ERROR; } @@ -1959,7 +1961,7 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) spath.len = p - path; nentries++; - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 1 '%s'", path); + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 3 '%s'", ppath->data); if (!dir.valid_info && ngx_de_info(path, &dir) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, @@ -1968,15 +1970,12 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) continue; } - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 2 '%s'", path); if (ngx_de_is_dir(&dir)) { - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 3 '%s'", path); if (ngx_rtmp_hls_cleanup_dir(&spath, playlen) == 0) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup dir '%V'", &name); - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 4 '%s'", path); if (ngx_delete_dir(path) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ERR, ngx_cycle->log, ngx_errno, From 45b39f7762bafc02fd1a3201939691595b362e71 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 21:49:15 +0400 Subject: [PATCH 49/50] fixed deleting directory on win --- dash/ngx_rtmp_dash_module.c | 7 +++++++ hls/ngx_rtmp_hls_module.c | 12 ++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 6585fbc..d4443c7 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -1294,6 +1294,13 @@ ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) 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 diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 9bdba95..4fd0e3d 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -1914,13 +1914,11 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) "hls: cleanup path='%V' playlen=%M", ppath, playlen); - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 1 '%s'", ppath->data); if (ngx_open_dir(ppath, &dir) != NGX_OK) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, ngx_errno, "hls: cleanup open dir failed '%V'", ppath); return NGX_ERROR; } - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 2 '%s'", ppath->data); nentries = 0; nerased = 0; @@ -1961,7 +1959,6 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) spath.len = p - path; nentries++; - ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: cleanup test 3 '%s'", ppath->data); if (!dir.valid_info && ngx_de_info(path, &dir) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, @@ -1977,10 +1974,17 @@ ngx_rtmp_hls_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) ngx_log_debug1(NGX_LOG_DEBUG_RTMP, ngx_cycle->log, 0, "hls: 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, "hls: cleanup " ngx_delete_dir_n - " failed on '%V' ('%s')", &spath, path); + " failed on '%V'", &spath); } else { nerased++; } From b07e0e2dee3a2019dc2be34591b1c4e27730b2a0 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Thu, 28 Nov 2013 22:08:48 +0400 Subject: [PATCH 50/50] fixed style --- dash/ngx_rtmp_dash_module.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 0de9a27..5d7cc66 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -273,7 +273,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) #define NGX_RTMP_DASH_MANIFEST_VIDEO \ " \n" \ " width, codec_ctx->height, codec_ctx->frame_rate, - &ctx->name, + &ctx->name, codec_ctx->width, codec_ctx->height, codec_ctx->frame_rate, @@ -407,7 +407,7 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) if (ctx->has_audio) { p = ngx_slprintf(buffer, last, NGX_RTMP_DASH_MANIFEST_AUDIO, - &ctx->name, + &ctx->name, codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ? "40.2" : "6b", codec_ctx->sample_rate, @@ -1191,7 +1191,7 @@ ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, if (htype != 1) { return NGX_OK; } - + p = (u_char *) &delay; p[0] = in->buf->pos[4];