video & audio working

This commit is contained in:
Roman Arutyunyan 2013-11-12 17:20:16 +04:00
parent 6caee7cb88
commit c18fd3aad4
3 changed files with 209 additions and 158 deletions

View file

@ -31,6 +31,7 @@ typedef struct {
ngx_uint_t opened; ngx_uint_t opened;
ngx_uint_t mdat_size; ngx_uint_t mdat_size;
ngx_uint_t sample_count; ngx_uint_t sample_count;
ngx_uint_t sample_mask;
ngx_fd_t fd; ngx_fd_t fd;
char type; char type;
uint32_t earliest_pres_time; uint32_t earliest_pres_time;
@ -170,7 +171,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
ngx_fd_t fd; ngx_fd_t fd;
ngx_str_t playlist, playlist_bak; ngx_str_t playlist, playlist_bak;
ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_dash_ctx_t *ctx;
ngx_rtmp_live_ctx_t *live_ctx;
ngx_rtmp_codec_ctx_t *codec_ctx; ngx_rtmp_codec_ctx_t *codec_ctx;
ngx_rtmp_dash_app_conf_t *dacf; 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); dacf = ngx_rtmp_get_module_app_conf(s, ngx_rtmp_dash_module);
ctx = ngx_rtmp_get_module_ctx(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); 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 || if (dacf == NULL || ctx == NULL || codec_ctx == NULL) {
live_ctx == NULL || live_ctx->stream == NULL)
{
return NGX_ERROR; return NGX_ERROR;
} }
@ -228,9 +225,9 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
" frameRate=\"%ui\"\n" \ " frameRate=\"%ui\"\n" \
" sar=\"1:1\"\n" \ " sar=\"1:1\"\n" \
" startWithSAP=\"1\"\n" \ " startWithSAP=\"1\"\n" \
" bandwidth=\"%ui\">\n" \ " bandwidth=\"0\">\n" \
" <SegmentTemplate\n" \ " <SegmentTemplate\n" \
" presentationTimeOffset=\"%ui\"\n" \ " presentationTimeOffset=\"0\"\n" \
" timescale=\"1000\"\n" \ " timescale=\"1000\"\n" \
" duration=\"%ui\"\n" \ " duration=\"%ui\"\n" \
" media=\"%V-$Number$.m4v\"\n" \ " media=\"%V-$Number$.m4v\"\n" \
@ -253,10 +250,10 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
" codecs=\"mp4a.%s\"\n" \ " codecs=\"mp4a.%s\"\n" \
" audioSamplingRate=\"%ui\"\n" \ " audioSamplingRate=\"%ui\"\n" \
" startWithSAP=\"1\"\n" \ " startWithSAP=\"1\"\n" \
" bandwidth=\"130685\">\n" \ " bandwidth=\"0\">\n" \
" <SegmentTemplate\n" \ " <SegmentTemplate\n" \
" presentationTimeOffset=\"%ui\"\n" \ " presentationTimeOffset=\"0\"\n" \
" timescale=\"%ui\"\n" \ " timescale=\"1000\"\n" \
" duration=\"%ui\"\n" \ " duration=\"%ui\"\n" \
" media=\"%V-$Number$.m4a\"\n" \ " media=\"%V-$Number$.m4a\"\n" \
" startNumber=\"0\"\n" \ " startNumber=\"0\"\n" \
@ -281,8 +278,6 @@ ngx_rtmp_dash_write_playlist(ngx_rtmp_session_t *s)
codec_ctx->width, codec_ctx->width,
codec_ctx->height, codec_ctx->height,
codec_ctx->frame_rate, 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, (ngx_uint_t) dacf->fraglen,
&ctx->name, &ctx->name,
&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 ? codec_ctx->audio_codec_id == NGX_RTMP_AUDIO_AAC ?
"40.2" : "6b", "40.2" : "6b",
codec_ctx->sample_rate, codec_ctx->sample_rate,
(ngx_uint_t) (ctx->audio.earliest_pres_time * (ngx_uint_t) dacf->fraglen,
(float) codec_ctx->sample_rate / 1000),
codec_ctx->sample_rate,
(ngx_uint_t) (dacf->fraglen * codec_ctx->sample_rate
/ 1000),
&ctx->name, &ctx->name,
&ctx->name); &ctx->name);
n = ngx_write_fd(fd, buffer, p - buffer); 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.audio = 0;
metadata.video = 1; metadata.video = 1;
/*TODO: buffer control*/
ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata);
ngx_rtmp_mp4_write_moov(s, &b, &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.video = 0;
metadata.audio = 1; metadata.audio = 1;
/*TODO: buffer control*/
ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata); ngx_rtmp_mp4_write_ftyp(&b, NGX_RTMP_MP4_FILETYPE_INIT, &metadata);
ngx_rtmp_mp4_write_moov(s, &b, &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 static void
ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t, ngx_rtmp_dash_close_fragment(ngx_rtmp_session_t *s, ngx_rtmp_dash_track_t *t)
ngx_uint_t sample_rate)
{ {
u_char *pos, *pos1; u_char *pos, *pos1;
size_t left; 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); "dash: fragment earliest pts: %uD", t->earliest_pres_time);
ngx_rtmp_mp4_write_moof(&b, t->earliest_pres_time, t->sample_count, 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; pos1 = b.last;
b.last = pos; b.last = pos;
ngx_rtmp_mp4_write_sidx(s, &b, t->mdat_size + 8 + (pos1 - (pos + 44)), ngx_rtmp_mp4_write_sidx(&b, t->mdat_size + 8 + (pos1 - (pos + 44)),
t->earliest_pres_time, t->latest_pres_time, t->earliest_pres_time, t->latest_pres_time);
sample_rate);
b.last = pos1; b.last = pos1;
ngx_rtmp_mp4_write_mdat(&b, t->mdat_size + 8); ngx_rtmp_mp4_write_mdat(&b, t->mdat_size + 8);
@ -518,21 +509,19 @@ done:
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)
{ {
ngx_rtmp_dash_ctx_t *ctx; ngx_rtmp_dash_ctx_t *ctx;
ngx_rtmp_codec_ctx_t *codec_ctx;
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"dash: close fragments"); "dash: close fragments");
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_dash_module);
codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module);
if (!ctx->opened) { if (!ctx->opened) {
return NGX_OK; return NGX_OK;
} }
ngx_rtmp_dash_close_fragment(s, &ctx->video, 0); ngx_rtmp_dash_close_fragment(s, &ctx->video);
ngx_rtmp_dash_close_fragment(s, &ctx->audio, codec_ctx->sample_rate); ngx_rtmp_dash_close_fragment(s, &ctx->audio);
ngx_rtmp_dash_write_playlist(s); 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->mdat_size = 0;
t->opened = 1; 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; 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 static ngx_int_t
ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, 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; u_char *p;
size_t size, bsize; size_t size, bsize;
@ -798,23 +797,18 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in,
size += bsize; 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) { if (!ctx->opened) {
ngx_rtmp_dash_open_fragments(s); 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) { if (t->sample_count == 0) {
t->earliest_pres_time = timestamp; t->earliest_pres_time = timestamp;
} }
t->latest_pres_time = timestamp;
if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) { if (t->sample_count < NGX_RTMP_DASH_MAX_SAMPLES) {
if (ngx_write_fd(t->fd, buffer, size) == NGX_ERROR) { 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].size = (uint32_t) size;
t->samples[t->sample_count].duration = 0; t->samples[t->sample_count].duration = 0;
t->samples[t->sample_count].timestamp = timestamp; 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->sample_count++;
t->mdat_size += (ngx_uint_t) size; t->mdat_size += (ngx_uint_t) size;

View file

@ -178,7 +178,7 @@ ngx_int_t
ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, 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; u_char *pos;
pos = b->last; 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 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; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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*/ /* time scale*/
ngx_rtmp_mp4_field_32(b, metadata->audio == 1 ? metadata->sample_rate : ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE);
NGX_RTMP_MP4_TIMESCALE);
ngx_rtmp_mp4_field_32(b, 0); /* duration */ /* duration */
ngx_rtmp_mp4_field_16(b, 0x15C7); /* language */ ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_16(b, 0); /* reserved */
/* lanuguage */
ngx_rtmp_mp4_field_16(b, 0x15C7);
/* reserved */
ngx_rtmp_mp4_field_16(b, 0);
ngx_rtmp_mp4_update_box_size(b, pos); 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_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; u_char *pos;
pos = b->last; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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) { if (metadata->video == 1) {
ngx_rtmp_mp4_write_vmhd(b); 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_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; u_char *pos;
pos = b->last; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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_hdlr(b, metadata);
ngx_rtmp_mp4_write_minf(s, 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_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; u_char *pos;
pos = b->last; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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_tkhd(b, metadata);
ngx_rtmp_mp4_write_mdia(s, 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 static 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)
{ {
u_char *pos; 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;
}
pos = b->last; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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 */ /* just write the trex and mehd in here too */
#if 0 #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, 0); /* version & flags */
ngx_rtmp_mp4_field_32(b, 0x000D8D2A); /* frag duration */ ngx_rtmp_mp4_field_32(b, 0x000D8D2A); /* frag duration */
#endif #endif
ngx_rtmp_mp4_field_32(b, 0x20); 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 */ /* version & flags */
ngx_rtmp_mp4_field_32(b, 1); /* track id */ ngx_rtmp_mp4_field_32(b, 0);
ngx_rtmp_mp4_field_32(b, 1); /* default sample description index */
ngx_rtmp_mp4_field_32(b, sample_dur); /* default sample duration */ /* track id */
ngx_rtmp_mp4_field_32(b, 0); /* default sample size */ ngx_rtmp_mp4_field_32(b, 1);
/* default sample flags */
ngx_rtmp_mp4_field_32(b, metadata->audio == 1 ? 0 : 0x00010000); /* 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); 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 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; 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); b->last = ngx_cpymem(b->last, "tfhd", sizeof("tfhd")-1);
/* version & flags */ /* version & flags */
ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? 0x00020020 : 0x00020000); ngx_rtmp_mp4_field_32(b, 0x00020000);
ngx_rtmp_mp4_field_32(b, 1); /* track id */
if (sample_rate > 0) { /* track id */
ngx_rtmp_mp4_field_32(b, 0x02000000); ngx_rtmp_mp4_field_32(b, 1);
}
ngx_rtmp_mp4_update_box_size(b, pos); 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 static ngx_int_t
ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, ngx_uint_t earliest_pres_time, ngx_rtmp_mp4_write_tfdt(ngx_buf_t *b, uint32_t earliest_pres_time)
ngx_uint_t sample_rate)
{ {
u_char *pos; u_char *pos;
float multiplier;
if (sample_rate > 0) {
multiplier = (float)sample_rate/(float)NGX_RTMP_MP4_TIMESCALE;
}
else {
multiplier = 1;
}
pos = b->last; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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 */ /* version == 1 aka 64 bit integer */
/* earliest presentation time */ ngx_rtmp_mp4_field_32(b, 0x00000000);
ngx_rtmp_mp4_field_32(b, (uint32_t)((float)earliest_pres_time*multiplier)); ngx_rtmp_mp4_field_32(b, earliest_pres_time);
ngx_rtmp_mp4_update_box_size(b, pos); 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 static ngx_int_t
ngx_rtmp_mp4_write_trun(ngx_buf_t *b, uint32_t sample_count, 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; u_char *pos;
uint32_t i, offset; uint32_t i, offset, nitems, flags;
pos = b->last; 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 */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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);
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, sample_count);
ngx_rtmp_mp4_field_32(b, offset); ngx_rtmp_mp4_field_32(b, offset);
for (i = 0; i < sample_count; i++) { for (i = 0; i < sample_count; i++, samples++) {
ngx_rtmp_mp4_field_32(b, samples[i].duration);
ngx_rtmp_mp4_field_32(b, samples[i].size); if (sample_mask & NGX_RTMP_MP4_SAMPLE_DURATION) {
ngx_rtmp_mp4_field_32(b, samples[i].key ? 0x00000000 : 0x00010000); ngx_rtmp_mp4_field_32(b, samples->duration);
ngx_rtmp_mp4_field_32(b, samples[i].delay); }
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); 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 static ngx_int_t
ngx_rtmp_mp4_write_traf(ngx_buf_t *b, ngx_uint_t earliest_pres_time, ngx_rtmp_mp4_write_traf(ngx_buf_t *b, uint32_t earliest_pres_time,
uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, u_char *moof_pos, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
ngx_uint_t sample_rate) ngx_uint_t sample_mask, u_char *moof_pos)
{ {
u_char *pos; u_char *pos;
pos = b->last; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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_tfhd(b);
ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time, sample_rate); ngx_rtmp_mp4_write_tfdt(b, earliest_pres_time);
ngx_rtmp_mp4_write_trun(b, sample_count, samples, moof_pos); ngx_rtmp_mp4_write_trun(b, sample_count, samples, sample_mask, moof_pos);
ngx_rtmp_mp4_update_box_size(b, 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); b->last = ngx_cpymem(b->last, "mfhd", sizeof("mfhd")-1);
ngx_rtmp_mp4_field_32(b, 0); /* don't know what this is */ /* don't know what this is */
ngx_rtmp_mp4_field_32(b, index); /* fragment index. */ ngx_rtmp_mp4_field_32(b, 0);
/* fragment index. */
ngx_rtmp_mp4_field_32(b, index);
ngx_rtmp_mp4_update_box_size(b, pos); 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_int_t
ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_write_sidx(ngx_buf_t *b, ngx_uint_t reference_size,
ngx_uint_t reference_size, ngx_uint_t earliest_pres_time, uint32_t earliest_pres_time, uint32_t latest_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 duration;
if (sample_rate > 0) { duration = latest_pres_time - earliest_pres_time;
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; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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 */ /* timescale */
ngx_rtmp_mp4_field_32(b, sample_rate > 0 ? sample_rate : ngx_rtmp_mp4_field_32(b, NGX_RTMP_MP4_TIMESCALE);
NGX_RTMP_MP4_TIMESCALE);
ngx_rtmp_mp4_field_32(b, ept); /* earliest presentation time */ /* earliest presentation time */
ngx_rtmp_mp4_field_32(b, 0); /* first offset */ ngx_rtmp_mp4_field_32(b, earliest_pres_time);
ngx_rtmp_mp4_field_16(b, 0); /* reserved */
ngx_rtmp_mp4_field_16(b, 1); /* reference count (=1) */ /* 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 */ /* 1st bit is reference type, the rest is reference size */
ngx_rtmp_mp4_field_32(b, 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) */ /* first bit is startsWithSAP (=1), next 3 bits are SAP type (=001) */
ngx_rtmp_mp4_field_8(b, 0x90); 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); 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_int_t
ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, ngx_rtmp_mp4_write_moof(ngx_buf_t *b, uint32_t earliest_pres_time,
uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples, uint32_t index, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
ngx_uint_t sample_rate) ngx_uint_t sample_mask, uint32_t index)
{ {
u_char *pos; u_char *pos;
pos = b->last; pos = b->last;
/* box size placeholder */ /* box size placeholder */
ngx_rtmp_mp4_field_32(b, 0); 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_mfhd(b, index);
ngx_rtmp_mp4_write_traf(b, earliest_pres_time, sample_count, samples, 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); ngx_rtmp_mp4_update_box_size(b, pos);

View file

@ -9,6 +9,12 @@
#include <ngx_rtmp.h> #include <ngx_rtmp.h>
#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 { typedef struct {
uint32_t size; uint32_t size;
uint32_t duration; 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_rtmp_mp4_metadata_t *metadata);
ngx_int_t ngx_rtmp_mp4_write_moov(ngx_rtmp_session_t *s, ngx_buf_t *b, 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);
ngx_int_t ngx_rtmp_mp4_write_moof(ngx_buf_t *b, ngx_uint_t earliest_pres_time, 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, uint32_t index, uint32_t sample_count, ngx_rtmp_mp4_sample_t *samples,
ngx_uint_t sample_rate); ngx_uint_t sample_mask, uint32_t index);
ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_int_t ngx_rtmp_mp4_write_sidx(ngx_buf_t *b,
ngx_uint_t reference_size, ngx_uint_t earliest_pres_time, ngx_uint_t reference_size, uint32_t earliest_pres_time,
ngx_uint_t latest_pres_time, ngx_uint_t sample_rate); uint32_t latest_pres_time);
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);