first video on mp4 streamer

This commit is contained in:
Roman Arutyunyan 2012-08-24 01:21:35 +04:00
parent 1968afdcc5
commit 82b55fce63

View file

@ -5,6 +5,7 @@
#include "ngx_rtmp_cmd_module.h"
#include "ngx_rtmp_live_module.h"
#include "ngx_rtmp_codec_module.h"
static ngx_rtmp_play_pt next_play;
@ -57,6 +58,26 @@ typedef struct {
} ngx_rtmp_mp4_times_t;
typedef struct {
uint32_t sample_count;
uint32_t sample_offset;
} ngx_rtmp_mp4_delay_entry_t;
typedef struct {
uint32_t version_flags;
uint32_t entry_count;
ngx_rtmp_mp4_delay_entry_t entries[0];
} ngx_rtmp_mp4_delays_t;
typedef struct {
uint32_t version_flags;
uint32_t entry_count;
uint32_t entries[0];
} ngx_rtmp_mp4_keys_t;
typedef struct {
uint32_t version_flags;
uint32_t sample_size;
@ -94,9 +115,13 @@ typedef struct {
uint32_t duration;
off_t offset;
size_t size;
ngx_int_t key;
uint32_t delay;
ngx_uint_t pos;
ngx_uint_t key_pos;
ngx_uint_t chunk;
ngx_uint_t chunk_pos;
ngx_uint_t chunk_count;
@ -104,16 +129,26 @@ typedef struct {
ngx_uint_t time_pos;
ngx_uint_t time_count;
ngx_uint_t delay_pos;
ngx_uint_t delay_count;
ngx_uint_t size_pos;
} ngx_rtmp_mp4_cursor_t;
typedef struct {
ngx_int_t type;
ngx_int_t codec;
uint32_t csid;
u_char fhdr;
u_char *header;
size_t header_size;
ngx_int_t header_sent;
ngx_rtmp_mp4_times_t *times;
ngx_rtmp_mp4_delays_t *delays;
ngx_rtmp_mp4_keys_t *keys;
ngx_rtmp_mp4_chunks_t *chunks;
ngx_rtmp_mp4_sizes_t *sizes;
ngx_rtmp_mp4_sizes2_t *sizes2;
@ -133,6 +168,12 @@ typedef struct {
ngx_rtmp_mp4_track_t *track;
ngx_uint_t ntracks;
ngx_uint_t width;
ngx_uint_t height;
ngx_uint_t nchannels;
ngx_uint_t sample_size;
ngx_uint_t sample_rate;
uint32_t start_timestamp, epoch;
ngx_event_t write_evt;
@ -158,6 +199,18 @@ typedef struct {
((n) & 0x00ff0000ull) >> 8 | \
((n) & 0xff000000ull) >> 24)
*/
static inline uint16_t
ngx_rtmp_r16(uint16_t n)
{
uint16_t ret;
/*TODO: optimize */
ngx_rtmp_rmemcpy(&ret, &n, 2);
return ret;
}
static inline uint32_t
ngx_rtmp_r32(uint32_t n)
{
@ -191,10 +244,16 @@ static ngx_int_t ngx_rtmp_mp4_parse_trak(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_hdlr(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_stsd(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_stsc(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_stts(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_ctts(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_stss(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_stsz(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_stz2(ngx_rtmp_session_t *s, u_char *pos,
@ -203,6 +262,20 @@ static ngx_int_t ngx_rtmp_mp4_parse_stco(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_co64(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_avc1(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_avcC(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_mp4a(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_esds(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_mp3(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_nmos(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
static ngx_int_t ngx_rtmp_mp4_parse_spex(ngx_rtmp_session_t *s, u_char *pos,
u_char *last);
typedef ngx_int_t (*ngx_rtmp_mp4_box_pt)(ngx_rtmp_session_t *s, u_char *pos,
@ -220,12 +293,22 @@ static ngx_rtmp_mp4_box_t ngx_rtmp_mp4_boxes[] = {
{ ngx_rtmp_mp4_make_tag('h','d','l','r'), ngx_rtmp_mp4_parse_hdlr },
{ ngx_rtmp_mp4_make_tag('m','i','n','f'), ngx_rtmp_mp4_parse },
{ ngx_rtmp_mp4_make_tag('s','t','b','l'), ngx_rtmp_mp4_parse },
{ ngx_rtmp_mp4_make_tag('s','t','s','d'), ngx_rtmp_mp4_parse_stsd },
{ ngx_rtmp_mp4_make_tag('s','t','s','c'), ngx_rtmp_mp4_parse_stsc },
{ ngx_rtmp_mp4_make_tag('s','t','t','s'), ngx_rtmp_mp4_parse_stts },
{ ngx_rtmp_mp4_make_tag('c','t','t','s'), ngx_rtmp_mp4_parse_ctts },
{ ngx_rtmp_mp4_make_tag('s','t','s','s'), ngx_rtmp_mp4_parse_stss },
{ ngx_rtmp_mp4_make_tag('s','t','s','z'), ngx_rtmp_mp4_parse_stsz },
{ ngx_rtmp_mp4_make_tag('s','t','z','2'), ngx_rtmp_mp4_parse_stz2 },
{ ngx_rtmp_mp4_make_tag('s','t','c','o'), ngx_rtmp_mp4_parse_stco },
{ ngx_rtmp_mp4_make_tag('c','o','6','4'), ngx_rtmp_mp4_parse_co64 }
{ ngx_rtmp_mp4_make_tag('c','o','6','4'), ngx_rtmp_mp4_parse_co64 },
{ ngx_rtmp_mp4_make_tag('a','v','c','1'), ngx_rtmp_mp4_parse_avc1 },
{ ngx_rtmp_mp4_make_tag('a','v','c','C'), ngx_rtmp_mp4_parse_avcC },
{ ngx_rtmp_mp4_make_tag('m','p','4','a'), ngx_rtmp_mp4_parse_mp4a },
{ ngx_rtmp_mp4_make_tag('e','s','d','s'), ngx_rtmp_mp4_parse_esds },
{ ngx_rtmp_mp4_make_tag('.','m','p','3'), ngx_rtmp_mp4_parse_mp3 },
{ ngx_rtmp_mp4_make_tag('n','m','o','s'), ngx_rtmp_mp4_parse_nmos },
{ ngx_rtmp_mp4_make_tag('s','p','e','x'), ngx_rtmp_mp4_parse_spex }
};
@ -352,7 +435,7 @@ ngx_rtmp_mp4_parse_hdlr(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
return NGX_OK;
}
if (last - pos < 12) {
if (pos + 12 > last) {
return NGX_ERROR;
}
@ -361,7 +444,6 @@ ngx_rtmp_mp4_parse_hdlr(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
if (type == ngx_rtmp_mp4_make_tag('v','i','d','e')) {
ctx->track->type = NGX_RTMP_MSG_VIDEO;
ctx->track->csid = NGX_RTMP_LIVE_CSID_VIDEO;
ctx->track->fhdr = 2; /* TODO; Sorenson */
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: video track");
@ -369,7 +451,6 @@ ngx_rtmp_mp4_parse_hdlr(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
} else if (type == ngx_rtmp_mp4_make_tag('s','o','u','n')) {
ctx->track->type = NGX_RTMP_MSG_AUDIO;
ctx->track->csid = NGX_RTMP_LIVE_CSID_AUDIO;
ctx->track->fhdr = 0x2e; /* TODO: mono, 16bit, 44K, MP3 */
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: audio track");
@ -382,6 +463,218 @@ ngx_rtmp_mp4_parse_hdlr(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
static ngx_int_t
ngx_rtmp_mp4_parse_video(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
ngx_int_t codec)
{
ngx_rtmp_mp4_ctx_t *ctx;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
if (ctx->track == NULL) {
return NGX_OK;
}
ctx->track->codec = codec;
if (pos + 78 > last) {
return NGX_ERROR;
}
pos += 24;
ctx->width = ngx_rtmp_r16(*(uint16_t *) pos);
pos += 2;
ctx->height = ngx_rtmp_r16(*(uint16_t *) pos);
pos += 52;
ctx->track->fhdr = codec;
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: video settings codec=%i, width=%ui, height=%ui",
codec, ctx->width, ctx->height);
return ngx_rtmp_mp4_parse(s, pos, last);
}
static ngx_int_t
ngx_rtmp_mp4_parse_audio(ngx_rtmp_session_t *s, u_char *pos, u_char *last,
ngx_int_t codec)
{
ngx_rtmp_mp4_ctx_t *ctx;
u_char *p;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
if (ctx->track == NULL) {
return NGX_OK;
}
ctx->track->codec = codec;
if (pos + 28 > last) {
return NGX_ERROR;
}
pos += 16;
ctx->nchannels = ngx_rtmp_r16(*(uint16_t *) pos);
pos += 2;
ctx->sample_size = ngx_rtmp_r16(*(uint16_t *) pos);
pos += 6;
ctx->sample_rate = ngx_rtmp_r16(*(uint16_t *) pos);
pos += 4;
p = &ctx->track->fhdr;
*p = 0;
if (ctx->nchannels) {
*p |= 0x01;
}
if (ctx->sample_size == 16) {
*p |= 0x02;
}
switch (ctx->sample_rate) {
case 11025:
*p |= 0x04;
break;
case 22050:
*p |= 0x08;
break;
case 44100:
*p |= 0x0c;
break;
}
*p |= (codec << 4);
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: audio settings codec=%i, nchannels==%ui, "
"sample_size=%ui, sample_rate=%ui",
codec, ctx->nchannels, ctx->sample_size, ctx->sample_rate);
return ngx_rtmp_mp4_parse(s, pos, last);
}
static ngx_int_t
ngx_rtmp_mp4_parse_avc1(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
return ngx_rtmp_mp4_parse_video(s, pos, last, NGX_RTMP_VIDEO_H264);
}
static ngx_int_t
ngx_rtmp_mp4_parse_avcC(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
ngx_rtmp_mp4_ctx_t *ctx;
if (pos == last) {
return NGX_OK;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
if (ctx->track == NULL || ctx->track->codec != NGX_RTMP_VIDEO_H264) {
return NGX_OK;
}
ctx->track->header = pos;
ctx->track->header_size = (size_t) (last - pos);
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: video h264 header size=%uz",
ctx->track->header_size);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mp4_parse_mp4a(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
return ngx_rtmp_mp4_parse_audio(s, pos, last, NGX_RTMP_AUDIO_MP3);
}
static ngx_int_t
ngx_rtmp_mp4_parse_esds(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
ngx_rtmp_mp4_ctx_t *ctx;
if (pos == last) {
return NGX_OK;
}
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
if (ctx->track == NULL || ctx->track->codec != NGX_RTMP_AUDIO_AAC) {
return NGX_OK;
}
/*TODO: parse AAC header? */
ctx->track->header = pos;
ctx->track->header_size = (size_t) (last - pos);
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: audio AAC header size=%uz",
ctx->track->header_size);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mp4_parse_mp3(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
return ngx_rtmp_mp4_parse_audio(s, pos, last, NGX_RTMP_AUDIO_MP3);
}
static ngx_int_t
ngx_rtmp_mp4_parse_nmos(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
return ngx_rtmp_mp4_parse_audio(s, pos, last, NGX_RTMP_AUDIO_NELLY);
}
static ngx_int_t
ngx_rtmp_mp4_parse_spex(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
return ngx_rtmp_mp4_parse_audio(s, pos, last, NGX_RTMP_AUDIO_SPEEX);
}
static ngx_int_t
ngx_rtmp_mp4_parse_stsd(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
if (pos + 8 > last) {
return NGX_ERROR;
}
pos += 8;
ngx_rtmp_mp4_parse(s, pos, last);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mp4_parse_stsc(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
@ -444,6 +737,68 @@ ngx_rtmp_mp4_parse_stts(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
static ngx_int_t
ngx_rtmp_mp4_parse_ctts(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
ngx_rtmp_mp4_ctx_t *ctx;
ngx_rtmp_mp4_track_t *t;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
t = ctx->track;
if (t == NULL) {
return NGX_OK;
}
t->delays = (ngx_rtmp_mp4_delays_t *) pos;
if (pos + sizeof(*t->delays) + ngx_rtmp_r32(t->delays->entry_count) *
sizeof(t->delays->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: delays entries=%uD",
ngx_rtmp_r32(t->delays->entry_count));
return NGX_OK;
}
t->delays = NULL;
return NGX_ERROR;
}
static ngx_int_t
ngx_rtmp_mp4_parse_stss(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
ngx_rtmp_mp4_ctx_t *ctx;
ngx_rtmp_mp4_track_t *t;
ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_mp4_module);
t = ctx->track;
if (t == NULL) {
return NGX_OK;
}
t->keys = (ngx_rtmp_mp4_keys_t *) pos;
if (pos + sizeof(*t->keys) + ngx_rtmp_r32(t->keys->entry_count) *
sizeof(t->keys->entries[0])
<= last)
{
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: keys entries=%uD",
ngx_rtmp_r32(t->keys->entry_count));
return NGX_OK;
}
t->keys = NULL;
return NGX_ERROR;
}
static ngx_int_t
ngx_rtmp_mp4_parse_stsz(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
@ -515,7 +870,6 @@ ngx_rtmp_mp4_parse_stz2(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
}
static ngx_int_t
ngx_rtmp_mp4_parse_stco(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
{
@ -596,8 +950,6 @@ ngx_rtmp_mp4_parse(ngx_rtmp_session_t *s, u_char *pos, u_char *last)
return NGX_ERROR;
}
/*TODO: implement 64-bit boxes */
hdr = (uint32_t *) pos;
size = ngx_rtmp_r32(hdr[0]);
tag = hdr[1];
@ -758,10 +1110,11 @@ ngx_rtmp_mp4_next_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr->time_count = 0;
}
ngx_log_debug4(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: next time time_pos=%ui timestamp=%D "
"time_count=%ui, pos=%ui",
cr->time_pos, cr->timestamp, cr->time_count, cr->pos);
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: next time time_pos=%ui, timestamp=%uD, "
"duration=%uD, time_count=%ui, pos=%ui",
cr->time_pos, cr->timestamp, cr->duration,
cr->time_count, cr->pos);
return NGX_OK;
}
@ -829,8 +1182,6 @@ ngx_rtmp_mp4_update_offset(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr = &t->cursor;
/*TODO: chunks start with 1, not 0 */
if (cr->chunk < 1) {
ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: update offset underflow");
@ -888,14 +1239,14 @@ ngx_rtmp_mp4_next_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
ngx_rtmp_mp4_chunk_entry_t *ce, *nce;
if (t->chunks == NULL) {
return NGX_ERROR;
return NGX_OK;
}
cr = &t->cursor;
if (cr->chunk_pos >= ngx_rtmp_r32(t->chunks->entry_count)) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: nect chunk overflow chunk_pos=%ui",
"mp4: next chunk overflow chunk_pos=%ui",
cr->chunk_pos);
return NGX_ERROR;
@ -943,7 +1294,8 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
cr = &t->cursor;
if (t->chunks == NULL || t->chunks->entry_count == 0) {
return NGX_ERROR;
cr->chunk = 1;
return NGX_OK;
}
ce = t->chunks->entries;
@ -962,6 +1314,7 @@ ngx_rtmp_mp4_seek_chunk(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
pos += dpos;
ce++;
cr->chunk_pos++;
}
if (ce->samples_per_chunk == 0) {
@ -1004,6 +1357,8 @@ ngx_rtmp_mp4_next_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_OK;
}
cr->size_pos++;
if (cr->size_pos >= ngx_rtmp_r32(t->sizes->sample_count)) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: next size overflow size_pos=%ui",
@ -1012,7 +1367,6 @@ ngx_rtmp_mp4_next_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
return NGX_ERROR;
}
cr->size_pos++;
cr->size = ngx_rtmp_r32(t->sizes->entries[cr->size_pos]);
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
@ -1095,12 +1449,154 @@ ngx_rtmp_mp4_seek_size(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
}
static ngx_int_t
ngx_rtmp_mp4_next_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
ngx_rtmp_mp4_cursor_t *cr;
cr = &t->cursor;
if (t->keys == NULL) {
return NGX_OK;
}
if (cr->key_pos >= ngx_rtmp_r32(t->keys->entry_count)) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: next key overflow key_pos=%ui",
cr->key_pos);
return NGX_OK;
}
cr->key = (cr->pos + 1 == ngx_rtmp_r32(t->keys->entries[cr->key_pos]));
if (cr->key) {
cr->key_pos++;
}
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: next key %s pos=%ui, key_pos=%ui",
cr->key ? "match" : "miss", cr->pos, cr->key_pos);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mp4_seek_key(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
ngx_rtmp_mp4_cursor_t *cr;
cr = &t->cursor;
if (t->keys == NULL) {
return NGX_OK;
}
while (cr->key_pos < ngx_rtmp_r32(t->keys->entry_count)) {
if (ngx_rtmp_r32(t->keys->entries[cr->key_pos]) >= cr->pos) {
break;
}
cr->key_pos++;
}
cr->key = (cr->key_pos < ngx_rtmp_r32(t->keys->entry_count) &&
cr->pos + 1 == ngx_rtmp_r32(t->keys->entries[cr->key_pos]));
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: seek key %s key_pos=%ui, pos=%ui",
cr->key ? "match" : "miss", cr->key_pos, cr->pos);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mp4_next_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
ngx_rtmp_mp4_cursor_t *cr;
cr = &t->cursor;
if (t->delays == NULL) {
return NGX_OK;
}
if (cr->delay_pos >= ngx_rtmp_r32(t->delays->entry_count)) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: next delay overflow key_pos=%ui",
cr->delay_pos);
return NGX_OK;
}
cr->delay_count++;
if (cr->delay_count >=
ngx_rtmp_r32(t->delays->entries[cr->delay_pos].sample_count))
{
cr->delay_pos++;
cr->delay_count = 0;
}
if (cr->delay_pos < ngx_rtmp_r32(t->delays->entry_count)) {
cr->delay = ngx_rtmp_r32(t->delays->entries[cr->delay_pos]
.sample_offset);
}
ngx_log_debug3(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: next delay delay_pos=%ui, delay_count=%ui, delay=%ui",
cr->delay_pos, cr->delay_count, cr->delay);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
ngx_rtmp_mp4_cursor_t *cr;
uint32_t pos, dpos;
cr = &t->cursor;
if (t->delays == NULL) {
return NGX_OK;
}
pos = 0;
while (cr->delay_pos < ngx_rtmp_r32(t->delays->entry_count)) {
dpos = ngx_rtmp_r32(t->delays->entries[cr->delay_pos].sample_count);
if (pos + dpos > cr->pos) {
cr->delay_count = cr->pos - pos;
cr->delay = ngx_rtmp_r32(t->delays->entries[cr->delay_pos]
.sample_offset);
break;
}
cr->delay_pos++;
pos += dpos;
}
ngx_log_debug2(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: seek delay delay_pos=%ui, delay_count=%ui",
cr->delay_pos, cr->delay_count);
return NGX_OK;
}
static ngx_int_t
ngx_rtmp_mp4_next(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t)
{
return ngx_rtmp_mp4_next_time(s, t) != NGX_OK ||
ngx_rtmp_mp4_next_key(s, t) != NGX_OK ||
ngx_rtmp_mp4_next_chunk(s, t) != NGX_OK ||
ngx_rtmp_mp4_next_size(s, t) != NGX_OK
ngx_rtmp_mp4_next_size(s, t) != NGX_OK ||
ngx_rtmp_mp4_next_delay(s, t) != NGX_OK
? NGX_ERROR : NGX_OK;
}
@ -1118,6 +1614,8 @@ ngx_rtmp_mp4_send(ngx_event_t *e)
ngx_rtmp_mp4_cursor_t *cr;
uint32_t buflen, end_timestamp, sched;
ssize_t ret;
u_char fhdr[5];
size_t fhdr_size;
ngx_uint_t n, abs_frame, active;
s = e->data;
@ -1156,11 +1654,6 @@ ngx_rtmp_mp4_send(ngx_event_t *e)
abs_frame = (cr->duration == 0);
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: read frame of track=%ui, "
"offset=%O, size=%uz, timestamp=%uD, duration=%uD",
n, cr->offset, cr->size, cr->timestamp, cr->duration);
ngx_memzero(&h, sizeof(h));
h.msid = NGX_RTMP_LIVE_MSID;
@ -1171,14 +1664,53 @@ ngx_rtmp_mp4_send(ngx_event_t *e)
h.timestamp = (abs_frame ? cr->timestamp : cr->duration);
if (cr->size > sizeof(ngx_rtmp_mp4_buffer) - 1) {
ngx_memzero(&in, sizeof(in));
ngx_memzero(&in_buf, sizeof(in_buf));
if (t->header && !t->header_sent) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: sending header size=%uz",
t->header_size);
fhdr[0] = t->fhdr | 0x10;
fhdr[1] = fhdr[2] = fhdr[3] = fhdr[4] = 0;
in.buf = &in_buf;
in_buf.pos = fhdr;
in_buf.last = fhdr + 5;
out = ngx_rtmp_append_shared_bufs(cscf, NULL, &in);
in.buf = &in_buf;
in_buf.pos = t->header;
in_buf.last = t->header + t->header_size;
ngx_rtmp_append_shared_bufs(cscf, out, &in);
ngx_rtmp_prepare_message(s, &h, NULL, out);
ngx_rtmp_send_message(s, out, 0);
ngx_rtmp_free_shared_chain(cscf, out);
t->header_sent = 1;
goto next;
}
ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: read frame of track=%ui, "
"offset=%O, size=%uz, timestamp=%uD, duration=%uD",
n, cr->offset, cr->size, cr->timestamp, cr->duration);
fhdr_size = (t->header ? 5 : 1);
if (cr->size + fhdr_size > sizeof(ngx_rtmp_mp4_buffer)) {
ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
"mp4: too big frame: %D>%uz",
cr->size, sizeof(ngx_rtmp_mp4_buffer));
continue;
}
ret = ngx_read_file(&ctx->file, ngx_rtmp_mp4_buffer + 1,
ret = ngx_read_file(&ctx->file, ngx_rtmp_mp4_buffer + fhdr_size,
cr->size, cr->offset);
if (ret != (ssize_t) cr->size) {
@ -1188,15 +1720,21 @@ ngx_rtmp_mp4_send(ngx_event_t *e)
}
ngx_rtmp_mp4_buffer[0] = t->fhdr;
/* TODO: handle video key flag */
ngx_memzero(&in, sizeof(in));
ngx_memzero(&in_buf, sizeof(in_buf));
if (cr->key) {
ngx_rtmp_mp4_buffer[0] |= 0x10;
}
if (fhdr_size > 1) {
ngx_rtmp_mp4_buffer[1] = 1;
ngx_rtmp_mp4_buffer[2] = cr->delay & 0xf00;
ngx_rtmp_mp4_buffer[3] = cr->delay & 0x0f0;
ngx_rtmp_mp4_buffer[4] = cr->delay & 0x00f;
}
in.buf = &in_buf;
in_buf.pos = ngx_rtmp_mp4_buffer;
in_buf.last = ngx_rtmp_mp4_buffer + cr->size + 1;
in_buf.last = ngx_rtmp_mp4_buffer + cr->size + fhdr_size;
out = ngx_rtmp_append_shared_bufs(cscf, NULL, &in);
@ -1208,11 +1746,11 @@ ngx_rtmp_mp4_send(ngx_event_t *e)
continue;
}
next:
active = 1;
next:
if (cr->timestamp > end_timestamp &&
(sched == 0 || cr->timestamp - end_timestamp < sched))
(sched == 0 || cr->timestamp < end_timestamp + sched))
{
sched = (uint32_t) (cr->timestamp - end_timestamp);
}
@ -1240,9 +1778,11 @@ ngx_rtmp_mp4_seek_track(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t,
cr = &t->cursor;
ngx_memzero(cr, sizeof(cr));
return ngx_rtmp_mp4_seek_time(s, t, timestamp) != NGX_OK ||
return ngx_rtmp_mp4_seek_time(s, t, timestamp * 90) != NGX_OK ||
ngx_rtmp_mp4_seek_key(s, t) != NGX_OK ||
ngx_rtmp_mp4_seek_chunk(s, t) != NGX_OK ||
ngx_rtmp_mp4_seek_size(s, t) != NGX_OK
ngx_rtmp_mp4_seek_size(s, t) != NGX_OK ||
ngx_rtmp_mp4_seek_delay(s, t) != NGX_OK
? NGX_ERROR : NGX_OK;
}
@ -1265,6 +1805,9 @@ ngx_rtmp_mp4_start(ngx_rtmp_session_t *s, ngx_int_t timestamp)
ngx_rtmp_mp4_stop(s);
for (n = 0; n < ctx->ntracks; ++n) {
ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0,
"mp4: seek track %ui", n);
ngx_rtmp_mp4_seek_track(s, &ctx->tracks[n], timestamp);
}