From a92d23d5301cb81b51123af99ce8282f25830255 Mon Sep 17 00:00:00 2001 From: Roman Arutyunyan Date: Fri, 24 Aug 2012 20:46:40 +0400 Subject: [PATCH] added AMF shortcuts & project-wide stream numbers; updated mp4 streamer --- ngx_rtmp.h | 25 ++++++++ ngx_rtmp_cmd_module.c | 5 +- ngx_rtmp_live_module.h | 8 +-- ngx_rtmp_mp4_module.c | 141 ++++++++++++++++------------------------- ngx_rtmp_play_module.c | 3 +- ngx_rtmp_send.c | 61 +++++++++++++++++- ngx_rtmp_streams.h | 28 ++++++++ 7 files changed, 173 insertions(+), 98 deletions(-) create mode 100644 ngx_rtmp_streams.h diff --git a/ngx_rtmp.h b/ngx_rtmp.h index 96a9aa5..f8f2061 100644 --- a/ngx_rtmp.h +++ b/ngx_rtmp.h @@ -375,6 +375,27 @@ void * ngx_rtmp_rmemcpy(void *dst, const void* src, size_t n); (((u_char*)ngx_rtmp_rmemcpy(dst, src, n)) + (n)) +static inline uint16_t +ngx_rtmp_r16(uint16_t n) +{ + return (n << 8) | (n >> 8); +} + + +static inline uint32_t +ngx_rtmp_r32(uint32_t n) +{ + return (n << 24) | ((n << 8) & 0xff0000) | ((n >> 8) & 0xff00) | (n >> 24); +} + + +static inline uint64_t +ngx_rtmp_r64(uint64_t n) +{ + return (uint64_t) ngx_rtmp_r32(n) << 32 | ngx_rtmp_r32(n >> 32); +} + + /* Receiving messages */ ngx_int_t ngx_rtmp_protocol_message_handler(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in); @@ -469,6 +490,10 @@ ngx_int_t ngx_rtmp_send_amf(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_int_t ngx_rtmp_receive_amf(ngx_rtmp_session_t *s, ngx_chain_t *in, ngx_rtmp_amf_elt_t *elts, size_t nelts); +/* AMF status sender */ +ngx_int_t ngx_rtmp_send_status(ngx_rtmp_session_t *s, char *code, + char* level, char *desc); + /* Frame types */ #define NGX_RTMP_VIDEO_KEY_FRAME 1 diff --git a/ngx_rtmp_cmd_module.c b/ngx_rtmp_cmd_module.c index dd70ffb..1995092 100644 --- a/ngx_rtmp_cmd_module.c +++ b/ngx_rtmp_cmd_module.c @@ -3,15 +3,12 @@ */ #include "ngx_rtmp_cmd_module.h" +#include "ngx_rtmp_streams.h" #define NGX_RTMP_FMS_VERSION "FMS/3,0,1,123" #define NGX_RTMP_CAPABILITIES 31 -#define NGX_RTMP_CMD_CSID_AMF_INI 3 -#define NGX_RTMP_CMD_CSID_AMF 5 -#define NGX_RTMP_CMD_MSID 1 - ngx_rtmp_connect_pt ngx_rtmp_connect; ngx_rtmp_create_stream_pt ngx_rtmp_create_stream; diff --git a/ngx_rtmp_live_module.h b/ngx_rtmp_live_module.h index 343efff..fdc5ffd 100644 --- a/ngx_rtmp_live_module.h +++ b/ngx_rtmp_live_module.h @@ -10,19 +10,13 @@ #include "ngx_rtmp.h" #include "ngx_rtmp_cmd_module.h" #include "ngx_rtmp_bandwidth.h" +#include "ngx_rtmp_streams.h" /* session flags */ #define NGX_RTMP_LIVE_PUBLISHING 0x01 -/* Chunk stream ids for output */ -#define NGX_RTMP_LIVE_CSID_META 5 -#define NGX_RTMP_LIVE_CSID_AUDIO 6 -#define NGX_RTMP_LIVE_CSID_VIDEO 7 -#define NGX_RTMP_LIVE_MSID 1 - - typedef struct ngx_rtmp_live_ctx_s ngx_rtmp_live_ctx_t; typedef struct ngx_rtmp_live_stream_s ngx_rtmp_live_stream_t; diff --git a/ngx_rtmp_mp4_module.c b/ngx_rtmp_mp4_module.c index 9e9835e..43b288a 100644 --- a/ngx_rtmp_mp4_module.c +++ b/ngx_rtmp_mp4_module.c @@ -4,8 +4,8 @@ #include "ngx_rtmp_cmd_module.h" -#include "ngx_rtmp_live_module.h" #include "ngx_rtmp_codec_module.h" +#include "ngx_rtmp_streams.h" static ngx_rtmp_play_pt next_play; @@ -118,6 +118,8 @@ typedef struct { ngx_int_t key; uint32_t delay; + unsigned valid:1; + ngx_uint_t pos; ngx_uint_t key_pos; @@ -186,56 +188,8 @@ typedef struct { } ngx_rtmp_mp4_ctx_t; -/* system stuff for mmapping; 4K pages assumed */ -/* TODO: more portable code */ -#define NGX_RTMP_PAGE_SHIFT 12 -#define NGX_RTMP_PAGE_SIZE (1 << NGX_RTMP_PAGE_SHIFT) -#define NGX_RTMP_PAGE_MASK (NGX_RTMP_PAGE_SIZE - 1) - - -#define ngx_rtmp_mp4_make_tag(a, b, c, d) ((uint32_t) d << 24 | \ - (uint32_t) c << 16 | \ - (uint32_t) b << 8 | \ - (uint32_t) a) - - -/* -#define ngx_rtmp_r32(n) (((n) & 0x000000ffull) << 24 | \ - ((n) & 0x0000ff00ull) << 8 | \ - ((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) -{ - uint32_t ret; - - /*TODO: optimize */ - ngx_rtmp_rmemcpy(&ret, &n, 4); - return ret; -} - - -static inline uint64_t -ngx_rtmp_r64(uint64_t n) -{ - uint64_t ret; - - ngx_rtmp_rmemcpy(&ret, &n, 8); - return ret; -} +#define ngx_rtmp_mp4_make_tag(a, b, c, d) \ + ((uint32_t)d << 24 | (uint32_t)c << 16 | (uint32_t)b << 8 | (uint32_t)a) static inline uint32_t @@ -252,8 +206,6 @@ ngx_rtmp_mp4_from_rtmp_timestamp(ngx_rtmp_mp4_track_t *t, uint32_t ts) } - - #define NGX_RTMP_MP4_DEFAULT_BUFLEN 1000 @@ -526,14 +478,14 @@ 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->csid = NGX_RTMP_CSID_VIDEO; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: video track"); } 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->csid = NGX_RTMP_CSID_AUDIO; ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: audio track"); @@ -1082,7 +1034,6 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s) offset = 0; size = 0; - /* find moov box */ for ( ;; ) { n = ngx_read_file(&ctx->file, (u_char *) &hdr, sizeof(hdr), offset); @@ -1093,8 +1044,6 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s) return NGX_ERROR; } - /*TODO: implement 64-bit boxes */ - size = ngx_rtmp_r32(hdr[0]); if (hdr[1] == ngx_rtmp_mp4_make_tag('m','o','o','v')) { @@ -1116,8 +1065,7 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s) size -= 8; offset += 8; - /* mmap moov box */ - page_offset = (offset & NGX_RTMP_PAGE_MASK); + page_offset = offset & (ngx_pagesize - 1); ctx->mmaped_size = page_offset + size; ctx->mmaped = mmap(NULL, ctx->mmaped_size, PROT_READ, MAP_SHARED, @@ -1131,7 +1079,6 @@ ngx_rtmp_mp4_init(ngx_rtmp_session_t *s) return NGX_ERROR; } - /* locate all required data within mapped area */ return ngx_rtmp_mp4_parse(s, (u_char *) ctx->mmaped + page_offset, (u_char *) ctx->mmaped + page_offset + size); } @@ -1214,7 +1161,6 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t, ngx_rtmp_mp4_cursor_t *cr; ngx_rtmp_mp4_time_entry_t *te; uint32_t dt; - ngx_uint_t dn; if (t->times == NULL) { return NGX_ERROR; @@ -1232,9 +1178,9 @@ ngx_rtmp_mp4_seek_time(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t, return NGX_ERROR; } - dn = (timestamp - cr->timestamp) / ngx_rtmp_r32(te->sample_delta); - cr->timestamp += ngx_rtmp_r32(te->sample_delta) * dn; - cr->pos += dn; + cr->time_count = (timestamp - cr->timestamp) / ngx_rtmp_r32(te->sample_delta); + cr->timestamp += ngx_rtmp_r32(te->sample_delta) * cr->time_count; + cr->pos += cr->time_count; break; } @@ -1758,12 +1704,18 @@ ngx_rtmp_mp4_seek_delay(ngx_rtmp_session_t *s, ngx_rtmp_mp4_track_t *t) 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_delay(s, t) != NGX_OK - ? NGX_ERROR : NGX_OK; + if (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_delay(s, t) != NGX_OK) + { + t->cursor.valid = 0; + return NGX_ERROR; + } + + t->cursor.valid = 1; + return NGX_OK; } @@ -1868,8 +1820,8 @@ ngx_rtmp_mp4_send_meta(ngx_rtmp_session_t *s) ngx_memzero(&h, sizeof(h)); - h.csid = NGX_RTMP_LIVE_CSID_META; - h.msid = NGX_RTMP_LIVE_MSID; + h.csid = NGX_RTMP_CSID_AMF; + h.msid = NGX_RTMP_MSID; h.type = NGX_RTMP_MSG_AMF_META; ngx_rtmp_prepare_message(s, &h, NULL, out); @@ -1928,7 +1880,7 @@ ngx_rtmp_mp4_send(ngx_event_t *e) for (n = 0; n < ctx->ntracks; ++n, ++t) { cr = &t->cursor; - if (cr->size == 0) { + if (!cr->valid) { continue; } @@ -1946,7 +1898,7 @@ ngx_rtmp_mp4_send(ngx_event_t *e) ngx_memzero(&h, sizeof(h)); - h.msid = NGX_RTMP_LIVE_MSID; + h.msid = NGX_RTMP_MSID; h.type = t->type; h.csid = t->csid; @@ -1989,8 +1941,7 @@ ngx_rtmp_mp4_send(ngx_event_t *e) ngx_log_debug5(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui read frame " "offset=%O, size=%uz, timestamp=%uD, duration=%uD", - t->id, cr->offset, cr->size, timestamp, - cr->duration); + t->id, cr->offset, cr->size, timestamp, duration); fhdr_size = (t->header ? 5 : 1); @@ -2012,8 +1963,14 @@ ngx_rtmp_mp4_send(ngx_event_t *e) ngx_rtmp_mp4_buffer[0] = t->fhdr; - if (cr->key) { - ngx_rtmp_mp4_buffer[0] |= 0x10; + if (h.type == NGX_RTMP_MSG_VIDEO) { + if (cr->key) { + ngx_rtmp_mp4_buffer[0] |= 0x10; + } else if (cr->delay) { + ngx_rtmp_mp4_buffer[0] |= 0x20; + } else { + ngx_rtmp_mp4_buffer[0] |= 0x30; + } } if (fhdr_size > 1) { @@ -2057,7 +2014,12 @@ next: again: if (active) { ngx_post_event(e, &ngx_posted_events); + return; } + + ngx_rtmp_send_user_stream_eof(s, NGX_RTMP_MSID); + + ngx_rtmp_send_status(s, "NetStream.Play.Stop", "status", "Stopped"); } @@ -2070,13 +2032,18 @@ 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, ngx_rtmp_mp4_from_rtmp_timestamp( + if (ngx_rtmp_mp4_seek_time(s, t, ngx_rtmp_mp4_from_rtmp_timestamp( t, timestamp)) != 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_delay(s, t) != NGX_OK - ? NGX_ERROR : 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_delay(s, t) != NGX_OK) + { + return NGX_ERROR; + } + + cr->valid = 1; + return NGX_OK; } @@ -2097,6 +2064,10 @@ ngx_rtmp_mp4_start(ngx_rtmp_session_t *s, ngx_int_t timestamp) ngx_rtmp_mp4_stop(s); + if (timestamp < 0) { + timestamp = 0; + } + for (n = 0; n < ctx->ntracks; ++n) { ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, "mp4: track#%ui seek", n); diff --git a/ngx_rtmp_play_module.c b/ngx_rtmp_play_module.c index 626cfc7..c631ed5 100644 --- a/ngx_rtmp_play_module.c +++ b/ngx_rtmp_play_module.c @@ -453,7 +453,8 @@ ngx_rtmp_play_send(ngx_event_t *e) if (n != sizeof(ngx_rtmp_play_header)) { ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, "play: could not read flv tag header"); - ngx_rtmp_send_user_stream_eof(s, 1); + ngx_rtmp_send_user_stream_eof(s, NGX_RTMP_MSID); + ngx_rtmp_send_status(s, "NetStream.Play.Stop", "status", "Stopped"); return; } diff --git a/ngx_rtmp_send.c b/ngx_rtmp_send.c index 8f9ba45..eab33bd 100644 --- a/ngx_rtmp_send.c +++ b/ngx_rtmp_send.c @@ -5,6 +5,7 @@ #include "ngx_rtmp.h" #include "ngx_rtmp_amf.h" +#include "ngx_rtmp_streams.h" #define NGX_RTMP_USER_START(s, tp) \ @@ -262,7 +263,9 @@ ngx_rtmp_append_amf(ngx_rtmp_session_t *s, return rc; } -ngx_int_t ngx_rtmp_send_amf(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + +ngx_int_t +ngx_rtmp_send_amf(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_rtmp_amf_elt_t *elts, size_t nelts) { ngx_chain_t *first; @@ -287,3 +290,59 @@ done: return rc; } + +ngx_int_t +ngx_rtmp_send_status(ngx_rtmp_session_t *s, char *code, char* level, char *desc) +{ + ngx_rtmp_header_t h; + static double trans; + + static ngx_rtmp_amf_elt_t out_inf[] = { + + { NGX_RTMP_AMF_STRING, + ngx_string("code"), + NULL, 0 }, + + { NGX_RTMP_AMF_STRING, + ngx_string("level"), + NULL, 0 }, + + { NGX_RTMP_AMF_STRING, + ngx_string("description"), + NULL, 0 }, + }; + + static ngx_rtmp_amf_elt_t out_elts[] = { + + { NGX_RTMP_AMF_STRING, + ngx_null_string, + "onStatus", 0 }, + + { NGX_RTMP_AMF_NUMBER, + ngx_null_string, + &trans, 0 }, + + { NGX_RTMP_AMF_NULL, + ngx_null_string, + NULL, 0 }, + + { NGX_RTMP_AMF_OBJECT, + ngx_null_string, + out_inf, + sizeof(out_inf) }, + }; + + + out_inf[0].data = code; + out_inf[1].data = level; + out_inf[2].data = desc; + + memset(&h, 0, sizeof(h)); + + h.type = NGX_RTMP_MSG_AMF_CMD; + h.csid = NGX_RTMP_CSID_AMF; + h.msid = NGX_RTMP_MSID; + + return ngx_rtmp_send_amf(s, &h, out_elts, + sizeof(out_elts) / sizeof(out_elts[0])); +} diff --git a/ngx_rtmp_streams.h b/ngx_rtmp_streams.h new file mode 100644 index 0000000..bf1ec7b --- /dev/null +++ b/ngx_rtmp_streams.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2012 Roman Arutyunyan + */ + + +#ifndef _NGX_RTMP_STREAMS_H_INCLUDED_ +#define _NGX_RTMP_STREAMS_H_INCLUDED_ + + +#define NGX_RTMP_MSID 1 + +#define NGX_RTMP_CSID_AMF_INI 3 +#define NGX_RTMP_CSID_AMF 5 +#define NGX_RTMP_CSID_AUDIO 6 +#define NGX_RTMP_CSID_VIDEO 7 + + +/*legacy*/ +#define NGX_RTMP_CMD_CSID_AMF_INI NGX_RTMP_CSID_AMF_INI +#define NGX_RTMP_CMD_CSID_AMF NGX_RTMP_CSID_AMF +#define NGX_RTMP_CMD_MSID NGX_RTMP_MSID +#define NGX_RTMP_LIVE_CSID_META NGX_RTMP_CSID_AMF +#define NGX_RTMP_LIVE_CSID_AUDIO NGX_RTMP_CSID_AUDIO +#define NGX_RTMP_LIVE_CSID_VIDEO NGX_RTMP_CSID_VIDEO +#define NGX_RTMP_LIVE_MSID NGX_RTMP_MSID + + +#endif /* _NGX_RTMP_STREAMS_H_INCLUDED_ */