diff --git a/dash/ngx_rtmp_dash_module.c b/dash/ngx_rtmp_dash_module.c index 4f9fb45..9301e69 100644 --- a/dash/ngx_rtmp_dash_module.c +++ b/dash/ngx_rtmp_dash_module.c @@ -475,16 +475,13 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) 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_sprintf(ctx->stream.data + ctx->stream.len, "init.m4v") = 0; fd = ngx_open_file(ctx->stream.data, NGX_FILE_RDWR, NGX_FILE_TRUNCATE, - NGX_FILE_DEFAULT_ACCESS); + NGX_FILE_DEFAULT_ACCESS); if (fd == NGX_INVALID_FILE) { ngx_log_error(NGX_LOG_ERR, s->connection->log, ngx_errno, @@ -496,10 +493,8 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) b.end = b.start + sizeof(buffer); b.pos = b.last = b.start; - 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); @@ -527,9 +522,7 @@ ngx_rtmp_dash_write_init_segments(ngx_rtmp_session_t *s) b.pos = b.last = b.start; 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); @@ -1088,7 +1081,7 @@ ngx_rtmp_dash_append(ngx_rtmp_session_t *s, ngx_chain_t *in, return NGX_ERROR; } - t->samples[t->sample_count].delay = 0; + t->samples[t->sample_count].delay = delay; t->samples[t->sample_count].size = (uint32_t) size; t->samples[t->sample_count].duration = 0; t->samples[t->sample_count].timestamp = timestamp; diff --git a/dash/ngx_rtmp_mp4.c b/dash/ngx_rtmp_mp4.c index 547f752..7feb3c4 100644 --- a/dash/ngx_rtmp_mp4.c +++ b/dash/ngx_rtmp_mp4.c @@ -6,31 +6,6 @@ #include -/* -static ngx_int_t -ngx_rtmp_mp4_field_64(ngx_buf_t *b, uint64_t n) -{ - 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; - - if (b->last + sizeof(bytes) > b->end) { - return NGX_ERROR; - } - - b->last = ngx_cpymem(b->last, bytes, sizeof(bytes)); - - return NGX_OK; -} -*/ - static ngx_int_t ngx_rtmp_mp4_field_32(ngx_buf_t *b, uint32_t n) { @@ -106,7 +81,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_put_descr(ngx_buf_t *b, int tag, size_t size) { ngx_rtmp_mp4_field_8(b, (uint8_t) tag); ngx_rtmp_mp4_field_8(b, size & 0x7F); @@ -222,7 +197,7 @@ ngx_rtmp_mp4_write_ftyp(ngx_buf_t *b, int type, ngx_rtmp_mp4_box(b, "iso5"); ngx_rtmp_mp4_field_32(b, 1); - if (metadata != NULL && metadata->video == 1) { + if (metadata != NULL && metadata->video) { ngx_rtmp_mp4_box(b, "avc1"); } @@ -256,27 +231,40 @@ ngx_rtmp_mp4_write_mvhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) 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 */ - ngx_rtmp_mp4_field_32(b, 0); /* modification time */ - ngx_rtmp_mp4_field_32(b, 1000); /* timescale */ - ngx_rtmp_mp4_field_32(b, 0); /* duration */ - 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 */ + /* 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); + + /* timescale */ + ngx_rtmp_mp4_field_32(b, 1000); + + /* duration */ + ngx_rtmp_mp4_field_32(b, 0); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0x00010000); + ngx_rtmp_mp4_field_16(b, 0x0100); + ngx_rtmp_mp4_field_16(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); 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 */ + /* 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_32(b, 0); - ngx_rtmp_mp4_field_32(b, 1); /* track id */ + /* next track id */ + ngx_rtmp_mp4_field_32(b, 1); ngx_rtmp_mp4_update_box_size(b, pos); @@ -291,29 +279,46 @@ ngx_rtmp_mp4_write_tkhd(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) pos = ngx_rtmp_mp4_start_box(b, "tkhd"); - 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 */ - /* 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 */ + /* version */ + ngx_rtmp_mp4_field_8(b, 0); + + /* flags: TrackEnabled */ + ngx_rtmp_mp4_field_24(b, 0x0000000f); + + /* creation time */ + ngx_rtmp_mp4_field_32(b, 0); + + /* modification time */ + ngx_rtmp_mp4_field_32(b, 0); + + /* track id */ + ngx_rtmp_mp4_field_32(b, 1); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + + /* duration */ + 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); + + /* reserved */ + ngx_rtmp_mp4_field_16(b, metadata->video ? 0 : 0x0100); + + /* reserved */ + ngx_rtmp_mp4_field_16(b, 0); 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 */ + if (metadata->video) { + ngx_rtmp_mp4_field_32(b, (uint32_t) metadata->width << 16); + ngx_rtmp_mp4_field_32(b, (uint32_t) metadata->height << 16); + } else { + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); } ngx_rtmp_mp4_update_box_size(b, pos); @@ -369,7 +374,7 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) /* pre defined */ ngx_rtmp_mp4_field_32(b, 0); - if (metadata->video == 1) { + if (metadata->video) { ngx_rtmp_mp4_box(b, "vide"); } else { ngx_rtmp_mp4_box(b, "soun"); @@ -380,7 +385,7 @@ ngx_rtmp_mp4_write_hdlr(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) ngx_rtmp_mp4_field_32(b, 0); ngx_rtmp_mp4_field_32(b, 0); - if (metadata->video == 1) { + if (metadata->video) { /* video handler string, NULL-terminated */ ngx_rtmp_mp4_data(b, "VideoHandler", sizeof("VideoHandler")); } @@ -582,49 +587,73 @@ static ngx_int_t ngx_rtmp_mp4_write_esds(ngx_rtmp_session_t *s, ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) { - int decoder_info; - int aac_header_offset; - u_char *pos; - ngx_chain_t *aac; + size_t dsi_len; + u_char *pos, *dsi; + ngx_buf_t *db; ngx_rtmp_codec_ctx_t *codec_ctx; codec_ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_codec_module); - if (codec_ctx == NULL) { + if (codec_ctx == NULL || codec_ctx->aac_header == 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); - aac_header_offset = 2; + db = codec_ctx->aac_header->buf; + if (db == NULL) { + return NGX_ERROR; } + dsi = db->pos + 2; + if (dsi > db->last) { + return NGX_ERROR; + } + + dsi_len = db->last - dsi; + pos = ngx_rtmp_mp4_start_box(b, "esds"); - ngx_rtmp_mp4_field_32(b, 0); /* version */ - /* length of the rest of the box */ - 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, 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? */ - /* Next two fields are bitrate. */ + /* version */ + ngx_rtmp_mp4_field_32(b, 0); + + + /* ES Descriptor */ + + ngx_rtmp_mp4_put_descr(b, 0x03, 23 + dsi_len); + + /* ES_ID */ + ngx_rtmp_mp4_field_16(b, 1); + + /* flags */ + ngx_rtmp_mp4_field_8(b, 0); + + + /* DecoderConfig Descriptor */ + + ngx_rtmp_mp4_put_descr(b, 0x04, 15 + dsi_len); + + /* objectTypeIndication: Audio ISO/IEC 14496-3 (AAC) */ + ngx_rtmp_mp4_field_8(b, 0x40); + + /* streamType: AudioStream */ + ngx_rtmp_mp4_field_8(b, 0x15); + + /* bufferSizeDB */ + ngx_rtmp_mp4_field_24(b, 0); + + /* maxBitrate */ ngx_rtmp_mp4_field_32(b, 0x0001F151); + + /* avgBitrate */ ngx_rtmp_mp4_field_32(b, 0x0001F14D); - if (aac) { - ngx_rtmp_mp4_put_descr(b, 0x05, decoder_info); - ngx_rtmp_mp4_data(b, aac->buf->pos + aac_header_offset, - (size_t) decoder_info); - } + + /* DecoderSpecificInfo Descriptor */ + + ngx_rtmp_mp4_put_descr(b, 0x05, dsi_len); + ngx_rtmp_mp4_data(b, dsi, dsi_len); + + + /* SL Descriptor */ ngx_rtmp_mp4_put_descr(b, 0x06, 1); ngx_rtmp_mp4_field_8(b, 0x02); @@ -643,25 +672,42 @@ ngx_rtmp_mp4_write_audio(ngx_rtmp_session_t *s, ngx_buf_t *b, pos = ngx_rtmp_mp4_start_box(b, "mp4a"); - 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, (uint16_t) metadata->sample_rate); - ngx_rtmp_mp4_field_16(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); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + ngx_rtmp_mp4_field_32(b, 0); + + /* reserved = 2*/ + ngx_rtmp_mp4_field_16(b, 2); + + /* reserved = 16 */ + ngx_rtmp_mp4_field_16(b, 16); + + /* reserved */ + ngx_rtmp_mp4_field_32(b, 0); + + /* time scale */ + ngx_rtmp_mp4_field_16(b, 1000); + + /* reserved */ + ngx_rtmp_mp4_field_16(b, 0); 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 */ + /* tag size*/ + ngx_rtmp_mp4_field_32(b, 8); + + /* null tag */ + ngx_rtmp_mp4_field_32(b, 0); ngx_rtmp_mp4_update_box_size(b, pos); + return NGX_OK; } @@ -674,14 +720,16 @@ ngx_rtmp_mp4_write_stsd(ngx_rtmp_session_t *s, ngx_buf_t *b, 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 */ + /* version & flags */ + ngx_rtmp_mp4_field_32(b, 0); - if (metadata->video == 1) { - ngx_rtmp_mp4_write_video(s,b,metadata); - } - else { - ngx_rtmp_mp4_write_audio(s,b,metadata); + /* entry count */ + ngx_rtmp_mp4_field_32(b, 1); + + if (metadata->video) { + 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); @@ -783,10 +831,9 @@ ngx_rtmp_mp4_write_minf(ngx_rtmp_session_t *s, ngx_buf_t *b, pos = ngx_rtmp_mp4_start_box(b, "minf"); - if (metadata->video == 1) { + if (metadata->video) { ngx_rtmp_mp4_write_vmhd(b); - } - else { + } else { ngx_rtmp_mp4_write_smhd(b); } @@ -840,16 +887,6 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) pos = ngx_rtmp_mp4_start_box(b, "mvex"); - /* 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); ngx_rtmp_mp4_box(b, "trex"); @@ -867,7 +904,7 @@ ngx_rtmp_mp4_write_mvex(ngx_buf_t *b, ngx_rtmp_mp4_metadata_t *metadata) ngx_rtmp_mp4_field_32(b, 0); /* default sample size, 1024 for AAC */ - ngx_rtmp_mp4_field_32(b, 1024); + ngx_rtmp_mp4_field_32(b, 0); /* default sample flags, key on */ ngx_rtmp_mp4_field_32(b, 0); diff --git a/dash/ngx_rtmp_mp4.h b/dash/ngx_rtmp_mp4.h index efcfcd6..31e6e02 100644 --- a/dash/ngx_rtmp_mp4.h +++ b/dash/ngx_rtmp_mp4.h @@ -27,11 +27,7 @@ typedef struct { 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_rtmp_mp4_metadata_t;