style changes

This commit is contained in:
Stephen Basile 2013-10-08 16:15:44 -04:00
parent 0ac369cd5a
commit 055228cc8a
3 changed files with 294 additions and 128 deletions

View file

@ -1,3 +1,5 @@
#include <ngx_config.h> #include <ngx_config.h>
#include <ngx_core.h> #include <ngx_core.h>
#include <ngx_rtmp.h> #include <ngx_rtmp.h>
@ -5,21 +7,25 @@
#include "ngx_rtmp_live_module.h" #include "ngx_rtmp_live_module.h"
#include "ngx_rtmp_mp4.h" #include "ngx_rtmp_mp4.h"
static ngx_rtmp_publish_pt next_publish; static ngx_rtmp_publish_pt next_publish;
static ngx_rtmp_close_stream_pt next_close_stream; static ngx_rtmp_close_stream_pt next_close_stream;
static ngx_rtmp_stream_begin_pt next_stream_begin; static ngx_rtmp_stream_begin_pt next_stream_begin;
static ngx_rtmp_stream_eof_pt next_stream_eof; static ngx_rtmp_stream_eof_pt next_stream_eof;
static ngx_int_t ngx_rtmp_dash_postconfiguration(ngx_conf_t *cf); 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 void * ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf);
static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf, static char * ngx_rtmp_dash_merge_app_conf(ngx_conf_t *cf,
void *parent, void *child); void *parent, void *child);
#define NGX_RTMP_DASH_BUFSIZE (1024*1024) #define NGX_RTMP_DASH_BUFSIZE (1024*1024)
#define NGX_RTMP_DASH_DIR_ACCESS 0744 #define NGX_RTMP_DASH_DIR_ACCESS 0744
#define NGX_RTMP_DASH_MAX_SIZE (800*1024) #define NGX_RTMP_DASH_MAX_SIZE (800*1024)
#define NGX_RTMP_DASH_MAX_SAMPLES 512 #define NGX_RTMP_DASH_MAX_SAMPLES 512
typedef struct { typedef struct {
ngx_uint_t video_earliest_pres_time; ngx_uint_t video_earliest_pres_time;
ngx_uint_t video_latest_pres_time; ngx_uint_t video_latest_pres_time;
@ -29,6 +35,7 @@ typedef struct {
uint32_t id; uint32_t id;
} ngx_rtmp_dash_frag_t; } ngx_rtmp_dash_frag_t;
typedef struct { typedef struct {
ngx_str_t playlist; ngx_str_t playlist;
ngx_str_t playlist_bak; ngx_str_t playlist_bak;
@ -60,11 +67,13 @@ typedef struct {
ngx_str_t audio_fragment; ngx_str_t audio_fragment;
} ngx_rtmp_dash_ctx_t; } ngx_rtmp_dash_ctx_t;
typedef struct { typedef struct {
ngx_str_t path; ngx_str_t path;
ngx_msec_t playlen; ngx_msec_t playlen;
} ngx_rtmp_dash_cleanup_t; } ngx_rtmp_dash_cleanup_t;
typedef struct { typedef struct {
ngx_flag_t dash; ngx_flag_t dash;
ngx_msec_t fraglen; ngx_msec_t fraglen;
@ -75,6 +84,7 @@ typedef struct {
ngx_path_t *slot; ngx_path_t *slot;
} ngx_rtmp_dash_app_conf_t; } ngx_rtmp_dash_app_conf_t;
static ngx_command_t ngx_rtmp_dash_commands[] = { static ngx_command_t ngx_rtmp_dash_commands[] = {
{ ngx_string("dash"), { ngx_string("dash"),
@ -105,7 +115,6 @@ static ngx_command_t ngx_rtmp_dash_commands[] = {
offsetof(ngx_rtmp_dash_app_conf_t, playlen), offsetof(ngx_rtmp_dash_app_conf_t, playlen),
NULL }, NULL },
{ ngx_string("dash_cleanup"), { ngx_string("dash_cleanup"),
NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1, NGX_RTMP_MAIN_CONF|NGX_RTMP_SRV_CONF|NGX_RTMP_APP_CONF|NGX_CONF_TAKE1,
ngx_conf_set_flag_slot, ngx_conf_set_flag_slot,
@ -116,6 +125,7 @@ static ngx_command_t ngx_rtmp_dash_commands[] = {
ngx_null_command ngx_null_command
}; };
static ngx_rtmp_module_t ngx_rtmp_dash_module_ctx = { static ngx_rtmp_module_t ngx_rtmp_dash_module_ctx = {
NULL, /* preconfiguration */ NULL, /* preconfiguration */
ngx_rtmp_dash_postconfiguration, /* postconfiguration */ ngx_rtmp_dash_postconfiguration, /* postconfiguration */
@ -146,6 +156,7 @@ ngx_module_t ngx_rtmp_dash_module = {
NGX_MODULE_V1_PADDING NGX_MODULE_V1_PADDING
}; };
static ngx_rtmp_dash_frag_t * static ngx_rtmp_dash_frag_t *
ngx_rtmp_dash_get_frag(ngx_rtmp_session_t *s, ngx_int_t n) 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)]; return &ctx->frags[(ctx->frag + n) % (hacf->winfrags * 2 + 1)];
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_rename_file(u_char *src, u_char *dst) 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 #endif
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s) 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; return NGX_ERROR;
} }
#define NGX_RTMP_DASH_MANIFEST_HEADER \ #define NGX_RTMP_DASH_MANIFEST_HEADER \
"<?xml version=\"1.0\"?>\n"\ "<?xml version=\"1.0\"?>\n" \
"<MPD type=\"dynamic\" xmlns=\"urn:mpeg:dash:schema:mpd:2011\" minimumUpdatePeriod=\"PT30M0S\" availabilityStartTime=\"%V\" minBufferTime=\"PT3S\" mediaPresentationDuration=\"PT0H0M0.00S\" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n"\ "<MPD type=\"dynamic\" xmlns=\"urn:mpeg:dash:schema:mpd:2011\" " \
"minimumUpdatePeriod=\"PT30M0S\" availabilityStartTime=\"%V\" " \
"minBufferTime=\"PT3S\" mediaPresentationDuration=\"PT0H0M0.00S\" " \
"profiles=\"urn:mpeg:dash:profile:isoff-live:2011\">\n" \
" <Period start=\"PT0S\" id=\"dash\" duration=\"PT0H0M0.00S\">\n" " <Period start=\"PT0S\" id=\"dash\" duration=\"PT0H0M0.00S\">\n"
#define NGX_RTMP_DASH_MANIFEST_VIDEO \ #define NGX_RTMP_DASH_MANIFEST_VIDEO \
" <AdaptationSet segmentAlignment=\"true\" maxWidth=\"%uL\" maxHeight=\"%uL\" maxFrameRate=\"%uL\">\n"\ " <AdaptationSet segmentAlignment=\"true\" maxWidth=\"%uL\" " \
" <Representation id=\"video\" mimeType=\"video/mp4\" codecs=\"avc1.42c028\" width=\"%uL\" height=\"%uL\" frameRate=\"%uL\" sar=\"1:1\" startWithSAP=\"1\" bandwidth=\"%uL\">\n"\ "maxHeight=\"%uL\" maxFrameRate=\"%uL\">\n" \
" <SegmentTemplate presentationTimeOffset=\"%uL\" timescale=\"1000\" duration=\"%uL\" media=\"%V-$Number$.m4v\" startNumber=\"0\" initialization=\"%V-init-video.dash\"/>\n"\ " <Representation id=\"video\" mimeType=\"video/mp4\" " \
" </Representation>\n"\ "codecs=\"avc1.42c028\" width=\"%uL\" height=\"%uL\" frameRate=\"%uL\" " \
"sar=\"1:1\" startWithSAP=\"1\" bandwidth=\"%uL\">\n" \
" <SegmentTemplate presentationTimeOffset=\"%uL\" timescale=\"1000\" " \
"duration=\"%uL\" media=\"%V-$Number$.m4v\" startNumber=\"0\" " \
"initialization=\"%V-init-video.dash\"/>\n" \
" </Representation>\n" \
" </AdaptationSet>\n" " </AdaptationSet>\n"
#define NGX_RTMP_DASH_MANIFEST_AUDIO \ #define NGX_RTMP_DASH_MANIFEST_AUDIO \
" <AdaptationSet segmentAlignment=\"true\">\n"\ " <AdaptationSet segmentAlignment=\"true\">\n"\
" <AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"1\"/>\n"\ " <AudioChannelConfiguration " \
" <Representation id=\"audio\" mimeType=\"audio/mp4\" codecs=\"mp4a.40.2\" audioSamplingRate=\"%uL\" startWithSAP=\"1\" bandwidth=\"130685\">\n"\ "schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" " \
" <SegmentTemplate presentationTimeOffset=\"%uL\" timescale=\"%uL\" duration=\"%uL\" media=\"%V-$Number$.m4a\" startNumber=\"0\" initialization=\"%V-init-audio.dash\"/>\n"\ "value=\"1\"/>\n" \
" </Representation>\n"\ " <Representation id=\"audio\" mimeType=\"audio/mp4\" " \
"codecs=\"mp4a.40.2\" audioSamplingRate=\"%uL\" startWithSAP=\"1\" " \
"bandwidth=\"130685\">\n" \
" <SegmentTemplate presentationTimeOffset=\"%uL\" timescale=\"%uL\" " \
"duration=\"%uL\" media=\"%V-$Number$.m4a\" startNumber=\"0\" " \
"initialization=\"%V-init-audio.dash\"/>\n" \
" </Representation>\n" \
" </AdaptationSet>\n" " </AdaptationSet>\n"
#define NGX_RTMP_DASH_MANIFEST_FOOTER \ #define NGX_RTMP_DASH_MANIFEST_FOOTER \
" </Period>\n"\ " </Period>\n" \
"</MPD>\n" "</MPD>\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*((float)(f->video_latest_pres_time/f->id)/1000.0)) : audio_dur = f->id > 0 ? (uint32_t)(codec_ctx->sample_rate *
(uint32_t)(codec_ctx->sample_rate*(hacf->fraglen/1000)); ((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, p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_HEADER,
&ctx->start_time); &ctx->start_time);
@ -239,28 +267,30 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
if (ctx->video) { if (ctx->video) {
p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_VIDEO, p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_VIDEO,
codec_ctx->width, codec_ctx->width,
codec_ctx->height, codec_ctx->height,
codec_ctx->frame_rate, codec_ctx->frame_rate,
codec_ctx->width, codec_ctx->width,
codec_ctx->height, codec_ctx->height,
codec_ctx->frame_rate, codec_ctx->frame_rate,
(uint32_t)(live_ctx->stream->bw_in.bandwidth*8), (uint32_t)(live_ctx->stream->bw_in.bandwidth*8),
f->video_earliest_pres_time, f->video_earliest_pres_time,
f->id > 0 ? (uint32_t)(f->video_latest_pres_time/f->id) : hacf->fraglen, f->id > 0 ? (uint32_t)(f->video_latest_pres_time /
&ctx->name, f->id) : hacf->fraglen,
&ctx->name); &ctx->name,
&ctx->name);
n = ngx_write_fd(fd, buffer, p - buffer); n = ngx_write_fd(fd, buffer, p - buffer);
} }
if (ctx->audio) { if (ctx->audio) {
p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO, p = ngx_snprintf(buffer, sizeof(buffer), NGX_RTMP_DASH_MANIFEST_AUDIO,
codec_ctx->sample_rate, codec_ctx->sample_rate,
(uint32_t)(f->audio_earliest_pres_time*((float)codec_ctx->sample_rate/1000.0)), (uint32_t)(f->audio_earliest_pres_time *
codec_ctx->sample_rate, ((float)codec_ctx->sample_rate/1000.0)),
audio_dur, codec_ctx->sample_rate,
&ctx->name, audio_dur,
&ctx->name); &ctx->name,
&ctx->name);
n = ngx_write_fd(fd, buffer, p - buffer); 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; return NGX_OK;
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s)
{ {
@ -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); ngx_snprintf(path, sizeof(path), "%Vinit-video.dash",&ctx->stream);
file.fd = ngx_open_file(path, NGX_FILE_RDWR, 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) { 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); ngx_snprintf(path, sizeof(path), "%Vinit-audio.dash",&ctx->stream);
file.fd = ngx_open_file(path, NGX_FILE_RDWR, 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) { if (file.fd == NGX_INVALID_FILE) {
@ -379,6 +410,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s)
return NGX_OK; return NGX_OK;
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_rewrite_segments(ngx_rtmp_session_t *s) 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); ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata);
pos = b->last; pos = b->last;
b->last += 44; /* leave room for sidx */ b->last += 44; /* leave room for sidx */
ngx_rtmp_mp4_write_moof(b, f->video_earliest_pres_time, ctx->video_sample_count, ngx_rtmp_mp4_write_moof(b, f->video_earliest_pres_time,
ctx->video_sample_sizes, (ctx->nfrags+ctx->frag),0); ctx->video_sample_count, ctx->video_sample_sizes,
(ctx->nfrags+ctx->frag),0);
pos1 = b->last; pos1 = b->last;
b->last = pos; b->last = pos;
ngx_rtmp_mp4_write_sidx(s, b, ctx->video_mdat_size+8+(pos1-(pos+44)), f->video_earliest_pres_time, ngx_rtmp_mp4_write_sidx(s, b, ctx->video_mdat_size+8+(pos1-(pos+44)),
f->video_latest_pres_time,0); f->video_earliest_pres_time,
f->video_latest_pres_time,0);
b->last = pos1; b->last = pos1;
ngx_rtmp_mp4_write_mdat(b, ctx->video_mdat_size+8); 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)); ngx_memzero(&file, sizeof(file));
file.log = s->connection->log; file.log = s->connection->log;
file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, 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) { if (file.fd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, 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 { do {
file_b.pos = file_b.last = file_b.start; file_b.pos = file_b.last = file_b.start;
if ((ssize_t)(written + sizeof(buffer)) > size) { 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; file_b.last += size-written;
} }
else { 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); file_b.last += sizeof(buffer);
} }
write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); 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); rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->video_fragment.data);
if (rc != NGX_OK) { if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, 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; 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); ngx_rtmp_mp4_write_ftyp(b, NGX_RTMP_MP4_FILETYPE_SEG, metadata);
pos = b->last; pos = b->last;
b->last += 44; /* leave room for sidx */ b->last += 44; /* leave room for sidx */
ngx_rtmp_mp4_write_moof(b, f->audio_earliest_pres_time, ctx->audio_sample_count, ngx_rtmp_mp4_write_moof(b, f->audio_earliest_pres_time,
ctx->audio_sample_sizes, (ctx->nfrags+ctx->frag), codec_ctx->sample_rate); ctx->audio_sample_count, ctx->audio_sample_sizes,
(ctx->nfrags+ctx->frag), codec_ctx->sample_rate);
pos1 = b->last; pos1 = b->last;
b->last = pos; b->last = pos;
ngx_rtmp_mp4_write_sidx(s, b, ctx->audio_mdat_size+8+(pos1-(pos+44)), f->audio_earliest_pres_time, ngx_rtmp_mp4_write_sidx(s, b, ctx->audio_mdat_size+8+(pos1-(pos+44)),
f->audio_latest_pres_time, codec_ctx->sample_rate); f->audio_earliest_pres_time,
f->audio_latest_pres_time, codec_ctx->sample_rate);
b->last = pos1; b->last = pos1;
ngx_rtmp_mp4_write_mdat(b, ctx->audio_mdat_size+8); 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)); ngx_memzero(&file, sizeof(file));
file.log = s->connection->log; file.log = s->connection->log;
file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, 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) { if (file.fd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, 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 { do {
file_b.pos = file_b.last = file_b.start; file_b.pos = file_b.last = file_b.start;
if ((ssize_t)(written + sizeof(buffer)) > size) { 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; file_b.last += size-written;
} }
else { 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); file_b.last += sizeof(buffer);
} }
write_size = ngx_rtmp_mp4_write_data(s, &file, &file_b); 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); rc = ngx_rtmp_dash_rename_file(ctx->stream.data, ctx->audio_fragment.data);
if (rc != NGX_OK) { if (rc != NGX_OK) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, 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_ERROR;
} }
return NGX_OK; return NGX_OK;
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_close_fragments(ngx_rtmp_session_t *s) 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; ctx->audio_file.log = s->connection->log;
*ngx_sprintf(ctx->stream.data + ctx->stream.len, "%uL.m4v", f->id) = 0; *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"); ngx_str_set(&ctx->video_file.name, "dash-v");
ctx->video_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, 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->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"); ngx_str_set(&ctx->audio_file.name, "dash-a");
ctx->audio_file.fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, 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) { if (ctx->audio_file.fd == NGX_INVALID_FILE) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, 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; return NGX_OK;
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_publish(ngx_rtmp_session_t *s, ngx_rtmp_publish_t *v) 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 + ctx->audio_fragment.len + NGX_INT64_LEN +
sizeof(".m4a")); 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] = '-'; 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] = '-'; ctx->audio_fragment.data[ctx->audio_fragment.len - 1] = '-';
/* playlist path */ /* playlist path */
@ -748,6 +797,7 @@ next:
return next_publish(s, v); return next_publish(s, v);
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_close_stream(ngx_rtmp_session_t *s, ngx_rtmp_close_stream_t *v) 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); return next_close_stream(s, v);
} }
static void 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_ctx_t *ctx;
ngx_rtmp_dash_app_conf_t *hacf; 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); f = ngx_rtmp_dash_get_frag(s, ctx->nfrags);
duration = ctx->video ? (f->video_latest_pres_time-f->video_earliest_pres_time) : duration = ctx->video ? (f->video_latest_pres_time -
(f->audio_latest_pres_time-f->audio_earliest_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->video) && ((int32_t)(hacf->fraglen - duration) > 150)) {
boundary = 0; 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; bsize = out.end - out.last;
} }
if (*in->buf->pos == 0xAF) { /* rtmp frame header */ 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; break;
} }
else { else {
if (bsize > 2) { 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; ctx->audio_sample_count += 1;
if ((ctx->audio_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES)) { 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_sample_sizes[ctx->audio_sample_count] =
ctx->audio_mdat_size += 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 { else {
ctx->audio_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; 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; return NGX_OK;
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_dash_video(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h,
ngx_chain_t *in) 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; ctx->video_sample_count += 1;
if (ctx->video_sample_count <= NGX_RTMP_DASH_MAX_SAMPLES) { 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_sample_sizes[ctx->video_sample_count] =
ctx->video_mdat_size += 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 { else {
ctx->video_sample_count = NGX_RTMP_DASH_MAX_SAMPLES; 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; return NGX_OK;
} }
static void static void
ngx_rtmp_dash_discontinue(ngx_rtmp_session_t *s) 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 static ngx_int_t
ngx_rtmp_dash_stream_begin(ngx_rtmp_session_t *s, ngx_rtmp_stream_begin_t *v) 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); return next_stream_eof(s, v);
} }
static ngx_int_t static ngx_int_t
ngx_rtmp_dash_cleanup_dir(ngx_str_t *ppath, ngx_msec_t playlen) 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 static time_t
ngx_rtmp_dash_cleanup(void *data) ngx_rtmp_dash_cleanup(void *data)
{ {
@ -1190,6 +1255,7 @@ ngx_rtmp_dash_cleanup(void *data)
return cleanup->playlen / 500; return cleanup->playlen / 500;
} }
static void * static void *
ngx_rtmp_dash_create_app_conf(ngx_conf_t *cf) 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; ngx_rtmp_stream_eof = ngx_rtmp_dash_stream_eof;
return NGX_OK; return NGX_OK;
} }

View file

@ -1,13 +1,16 @@
#include <ngx_config.h> #include <ngx_config.h>
#include <ngx_core.h> #include <ngx_core.h>
#include "ngx_rtmp_mp4.h" #include "ngx_rtmp_mp4.h"
#include <ngx_rtmp_codec_module.h> #include <ngx_rtmp_codec_module.h>
static u_char compressor_name[] = { 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 ngx_int_t
ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_field_24(ngx_buf_t *b, uint32_t n) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_field_16(ngx_buf_t *b, uint32_t n) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_field_8(ngx_buf_t *b, unsigned int n) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) { ngx_rtmp_mp4_put_descr(ngx_buf_t *b, int tag, unsigned int size) {
//int i = 3; //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); ngx_rtmp_mp4_field_8(b, tag);
//for (; i > 0; i--) { //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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_update_box_size(ngx_buf_t *b, u_char *pos) 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; return NGX_OK;
} }
/* transformation matrix /* transformation matrix
|a b u| |a b u|
|c d v| |c d v|
|tx ty w| */ |tx ty w| */
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_matrix(ngx_buf_t *buf, int16_t a, int16_t b, int16_t c, 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, a << 16); /* 16.16 format */
ngx_rtmp_mp4_field_32(buf, b << 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; return NGX_OK;
} }
ngx_int_t 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; 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); ngx_rtmp_mp4_field_32(b, 1);
if (metadata.video == 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 { else {
b->last = ngx_cpymem(b->last, "iso5dash", sizeof("iso5dash")-1); 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); ngx_rtmp_mp4_update_box_size(b, pos);
return NGX_OK; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) 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, 0); /* modification time */
ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE); /* timescale */ 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, 0); /* duration */
ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_PREFERRED_RATE); /* preferred playback 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); /* preferred volume 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_16(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 */
@ -206,6 +220,7 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata)
return NGX_OK; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) 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); /* duration */
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, 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, metadata.audio == 1 ? 0x0100 : 0);
ngx_rtmp_mp4_field_16(b, 0); /* reserved */ 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; return NGX_OK;
} }
ngx_int_t 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, 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); /* version */
ngx_rtmp_mp4_field_32(b, 0); /* creation time */ 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); /* 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_32(b, 0); /* duration */
ngx_rtmp_mp4_field_16(b, 0x15C7); /* language */ ngx_rtmp_mp4_field_16(b, 0x15C7); /* language */
ngx_rtmp_mp4_field_16(b, 0); /* reserved */ 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) 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); /* version and flags */
ngx_rtmp_mp4_field_32(b, 0); /* pre defined (=0) */ ngx_rtmp_mp4_field_32(b, 0); /* pre defined (=0) */
if (metadata.video == 1) { 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 { 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 */ 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 */ ngx_rtmp_mp4_field_32(b, 0); /* reserved */
if (metadata.video == 1) { 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 { 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); 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_vmhd(ngx_buf_t *b) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_smhd(ngx_buf_t *b) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_dref(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_dinf(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) 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; return NGX_OK;
} }
ngx_int_t 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; u_char *pos, *p;
ngx_rtmp_codec_ctx_t *codec_ctx; 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; return NGX_OK;
} }
ngx_int_t 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; 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; return NGX_OK;
} }
ngx_int_t 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)
{ {
/* SCREW THIS FUNCTION */
u_char *pos; u_char *pos;
ngx_rtmp_codec_ctx_t *codec_ctx; 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); b->last = ngx_cpymem(b->last, "esds", sizeof("esds")-1);
ngx_rtmp_mp4_field_32(b, 0); /* version */ 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_16(b, 1); /* track id */
ngx_rtmp_mp4_field_8(b, 0x00); /* flags */ 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, 0x40); /* codec id */
ngx_rtmp_mp4_field_8(b, 0x15); /* audio stream */ ngx_rtmp_mp4_field_8(b, 0x15); /* audio stream */
ngx_rtmp_mp4_field_24(b, 0); /* buffersize? */ 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 */ ngx_rtmp_mp4_field_32(b, 0x0001F14D); /* I really dont know */
if (aac) { if (aac) {
@ -530,8 +565,10 @@ ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b)
return NGX_OK; return NGX_OK;
} }
ngx_int_t 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; 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; return NGX_OK;
} }
ngx_int_t 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; 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_stts(ngx_buf_t *b) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_stsc(ngx_buf_t *b) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_stsz(ngx_buf_t *b) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_stco(ngx_buf_t *b) 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; return NGX_OK;
} }
ngx_int_t 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; 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; return NGX_OK;
} }
ngx_int_t 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; 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; return NGX_OK;
} }
ngx_int_t 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; 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_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; 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata) 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; uint32_t sample_dur;
if (metadata.video == 1) { 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 { else {
sample_dur = 1024; 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, 1); /* default sample description index */
ngx_rtmp_mp4_field_32(b, sample_dur); /* default sample duration */ 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, 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); ngx_rtmp_mp4_update_box_size(b, pos);
return NGX_OK; return NGX_OK;
} }
ngx_int_t 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; 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; return NGX_OK;
} }
ngx_int_t 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, 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); 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 */ ngx_rtmp_mp4_field_32(b, 1); /* track id */
if (sample_rate > 0) { if (sample_rate > 0) {
ngx_rtmp_mp4_field_32(b, 0x02000000); 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; return NGX_OK;
} }
ngx_int_t 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; u_char *pos;
float multiplier; 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); 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, 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); ngx_rtmp_mp4_update_box_size(b, pos);
return NGX_OK; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, uint32_t sample_sizes[128], ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count,
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; u_char *pos;
uint32_t i, offset; 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; pos = b->last;
if (sample_rate > 0) { 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 { 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); 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, sample_count); /* sample count */
ngx_rtmp_mp4_field_32(b, offset); /* data offset */ ngx_rtmp_mp4_field_32(b, offset); /* data offset */
if (sample_rate == 0) { 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; return NGX_OK;
} }
ngx_int_t 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], ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time,
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; 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_tfhd(b, sample_rate);
ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time, 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); ngx_rtmp_mp4_update_box_size(b, pos);
return NGX_OK; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_mfhd(ngx_buf_t *b, uint32_t index) 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; return NGX_OK;
} }
ngx_int_t ngx_int_t
ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t reference_size, ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b,
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; u_char *pos;
uint32_t ept, dur; uint32_t ept, dur;
if (sample_rate > 0) { if (sample_rate > 0) {
ept = (uint32_t)((float)earliest_pres_time*((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); ept = (uint32_t)((float)earliest_pres_time*((float)sample_rate /
dur = (uint32_t)((float)(latest_pres_time-earliest_pres_time)*((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE)); (float)NGX_RTMP_MP4_TIMESCALE));
dur = (uint32_t)((float)(latest_pres_time-earliest_pres_time) *
((float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE));
} }
else { else {
ept = earliest_pres_time; 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, 0); /* version */
ngx_rtmp_mp4_field_32(b, 1); /* reference id */ 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, ept); /* earliest presentation time */
ngx_rtmp_mp4_field_32(b, 0); /* first offset */ ngx_rtmp_mp4_field_32(b, 0); /* first offset */
ngx_rtmp_mp4_field_16(b, 0); /* reserved */ ngx_rtmp_mp4_field_16(b, 0); /* reserved */
ngx_rtmp_mp4_field_16(b, 1); /* reference count (=1) */ 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_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_field_24(b, 0); /* SAP delta time */
ngx_rtmp_mp4_update_box_size(b, pos); 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; return NGX_OK;
} }
ngx_int_t 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_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time,
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; 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); b->last = ngx_cpymem(b->last, "moof", sizeof("moof")-1);
ngx_rtmp_mp4_write_mfhd(b, index); 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); ngx_rtmp_mp4_update_box_size(b, pos);
return NGX_OK; return NGX_OK;
} }
ngx_uint_t ngx_uint_t
ngx_rtmp_mp4_write_mdat(ngx_buf_t *b, ngx_uint_t size) 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; return NGX_OK;
} }
uint32_t uint32_t
ngx_rtmp_mp4_write_data(ngx_rtmp_session_t *s, ngx_file_t *file, ngx_buf_t *b) 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; return size;
} }

View file

@ -1,4 +1,5 @@
#ifndef _NGX_RTMP_MP4_H_INCLUDED_ #ifndef _NGX_RTMP_MP4_H_INCLUDED_
#define _NGX_RTMP_MP4_H_INCLUDED_ #define _NGX_RTMP_MP4_H_INCLUDED_
@ -7,6 +8,7 @@
#include <ngx_core.h> #include <ngx_core.h>
#include <ngx_rtmp.h> #include <ngx_rtmp.h>
typedef struct { typedef struct {
ngx_uint_t width; ngx_uint_t width;
ngx_uint_t height; ngx_uint_t height;
@ -16,23 +18,38 @@ typedef struct {
ngx_uint_t frame_rate; ngx_uint_t frame_rate;
} ngx_rtmp_mp4_metadata_t; } ngx_rtmp_mp4_metadata_t;
enum { enum {
NGX_RTMP_MP4_FILETYPE_INIT = 0, NGX_RTMP_MP4_FILETYPE_INIT = 0,
NGX_RTMP_MP4_FILETYPE_SEG = 1 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); /* divide all times by this value. this is the same resolution as RTMP so it
ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t metadata); is convenient */
ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, uint32_t sample_count, #define NGX_RTMP_MP4_TIMESCALE 1000
uint32_t sample_sizes[128], uint32_t index, ngx_uint_t sample_rate); /* normal forward playback as defined by spec */
ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_uint_t reference_size, #define NGX_RTMP_MP4_PREFERRED_RATE 0x00010000
ngx_uint_t earliest_pres_time, ngx_uint_t latest_pres_time, ngx_uint_t sample_rate); /* 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); 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_ */ #endif /* _NGX_RTMP_MP4_H_INCLUDED_ */