From 174910f06423e211eeff5b2ffbd3dd87a899f817 Mon Sep 17 00:00:00 2001 From: gitaudo Date: Tue, 20 Oct 2020 12:29:34 +0200 Subject: [PATCH 1/8] Modified PMT table with support for metadata stream --- hls/ngx_rtmp_mpegts.c | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/hls/ngx_rtmp_mpegts.c b/hls/ngx_rtmp_mpegts.c index ae66f71b8..9589c5b44 100644 --- a/hls/ngx_rtmp_mpegts.c +++ b/hls/ngx_rtmp_mpegts.c @@ -41,17 +41,25 @@ static u_char ngx_rtmp_mpegts_header[] = { /* TS */ 0x47, 0x50, 0x01, 0x10, 0x00, /* PSI */ - 0x02, 0xb0, 0x17, 0x00, 0x01, 0xc1, 0x00, 0x00, + 0x02, 0xb0, 0x3c, 0x00, 0x01, 0xc1, 0x00, 0x00, // sec len da 0x17 a 0x3c /* PMT */ 0xe1, 0x00, - 0xf0, 0x00, + 0xf0, 0x11, // from 00 to 11 (prog info len = 17) + /* descriptor list */ + 0x25, 0x0f, 0xff, 0xff, 0x49, 0x44, 0x33, 0x20, 0xff, 0x49, + 0x44, 0x33, 0x20, 0x00, 0x1f, 0x00, 0x01, + 0x1b, 0xe1, 0x00, 0xf0, 0x00, /* h264 */ 0x0f, 0xe1, 0x01, 0xf0, 0x00, /* aac */ + 0x15, 0xe1, 0x02, 0xf0, 0x0f, /* metadata */ + 0x26, 0x0d, 0xff, 0xff, 0x49, 0x44, 0x33, 0x20, 0xff, 0x49, + 0x44, 0x33, 0x20, 0x00, 0x0f, /* stream descriptor list */ /*0x03, 0xe1, 0x01, 0xf0, 0x00,*/ /* mp3 */ /* CRC */ - 0x2f, 0x44, 0xb9, 0x9b, /* crc for aac */ + 0x22, 0xBB, 0x5B, 0x1A, + /* 0x2f, 0x44, 0xb9, 0x9b, crc for aac */ /*0x4e, 0x59, 0x3d, 0x1e,*/ /* crc for mp3 */ - /* stuffing 157 bytes */ + /* stuffing 120 bytes */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, @@ -63,11 +71,7 @@ static u_char ngx_rtmp_mpegts_header[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, - 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, }; From 564892fb68ea5fc4df520052e9f88a897a96a3a2 Mon Sep 17 00:00:00 2001 From: gitaudo Date: Tue, 20 Oct 2020 13:16:23 +0200 Subject: [PATCH 2/8] Added AMF metadata parsing --- hls/ngx_rtmp_hls_module.c | 97 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 97 insertions(+) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 25166cbdc..72a8ab768 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -10,6 +10,7 @@ #include #include #include "ngx_rtmp_mpegts.h" +#include "ngx_rtmp_amf.h" static ngx_rtmp_publish_pt next_publish; @@ -2429,6 +2430,95 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) } +static ngx_int_t +ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, + ngx_chain_t *in) +{ + ngx_rtmp_hls_ctx_t *ctx; + ngx_rtmp_mpegts_frame_t frame; + ngx_int_t rc; + ngx_buf_t out; + ngx_uint_t skip; + + static u_char buffer[132]; + + static struct { + char title[32]; + } v; + + ngx_memzero(&v, sizeof(v)); + + static ngx_rtmp_amf_elt_t in_inf[] = { + { NGX_RTMP_AMF_STRING, + ngx_string("StreamTitle"), + &v.title, 32 }, + }; + + static ngx_rtmp_amf_elt_t in_elts[] = { + { NGX_RTMP_AMF_STRING, + ngx_string("first_string"), + NULL, 0 }, + + { NGX_RTMP_AMF_OBJECT, + ngx_string("mix_array"), + in_inf, sizeof(in_inf) }, + }; + + skip = !(in->buf->last > in->buf->pos + && *in->buf->pos == NGX_RTMP_AMF_STRING); + + if (ngx_rtmp_receive_amf(s, in, in_elts + skip, + sizeof(in_elts) / sizeof(in_elts[0]) - skip)) + { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "codec: error parsing data frame"); + return NGX_OK; + } + + + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); + + if (ctx == NULL) { + return NGX_OK; + } + + ngx_memzero(&frame, sizeof(frame)); + frame.cc = ctx->meta_cc; + frame.dts = (uint64_t) h->timestamp * 90; + frame.pts = frame.dts; + frame.pid = 0x102; + frame.sid = 0xbd; + + ngx_rtmp_hls_update_fragment(s, frame.dts, 1, 1); + if (!ctx->opened) { + ngx_log_debug0(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "ctx not opened"); + return NGX_OK; + } + + ngx_memzero(&out, sizeof(out)); + + out.start = buffer; + out.end = buffer + sizeof(buffer); + out.pos = out.start; + out.last = out.pos; + + rc = ngx_rtmp_mpegts_write_frame(&ctx->file, &frame, &out); + + if (rc != NGX_OK) { + ngx_log_error(NGX_LOG_ERR, s->connection->log, 0, + "hls: ID3 frame write failed"); + } else { + ngx_log_debug1(NGX_LOG_DEBUG_RTMP, s->connection->log, 0, + "hls: ID3 frame write pts=%uL", frame.pts); + } + + ctx->meta_cc = frame.cc; + + return rc; +} + + static ngx_int_t ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf) { @@ -2443,6 +2533,13 @@ ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf) h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]); *h = ngx_rtmp_hls_audio; + ch = ngx_array_push(&cmcf->amf); + if (ch == NULL) { + return NGX_ERROR; + } + ngx_str_set(&ch->name, "onMetaData"); + ch->handler = ngx_rtmp_hls_meta; + next_publish = ngx_rtmp_publish; ngx_rtmp_publish = ngx_rtmp_hls_publish; From aa7b893daf9ba0e88c831eb0ab5de0e34a644fbc Mon Sep 17 00:00:00 2001 From: gitaudo Date: Tue, 20 Oct 2020 13:17:58 +0200 Subject: [PATCH 3/8] Added ID3 tag creation --- hls/ngx_rtmp_hls_module.c | 56 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 72a8ab768..83c1d935e 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -11,6 +11,7 @@ #include #include "ngx_rtmp_mpegts.h" #include "ngx_rtmp_amf.h" +#include "id3v2lib.h" static ngx_rtmp_publish_pt next_publish; @@ -2441,6 +2442,8 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_uint_t skip; static u_char buffer[132]; + ID3v2_tag* tag; + ID3v2_frame_list* frame_list; static struct { char title[32]; @@ -2496,6 +2499,18 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } + + tag = new_tag(); + tag_set_title(v.title, 3, tag); + // tag_set_artist("gabriele", 0, tag); + + tag->tag_header = new_header(); + memcpy(tag->tag_header->tag, "ID3", 3); + tag->tag_header->major_version = '\x04'; + tag->tag_header->minor_version = '\x00'; + tag->tag_header->flags = '\x00'; + tag->tag_header->tag_size = get_tag_size(tag)+1; + ngx_memzero(&out, sizeof(out)); out.start = buffer; @@ -2503,6 +2518,47 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, out.pos = out.start; out.last = out.pos; + // TODO: maybe move this section inside the id3 library + *out.last++ = tag->tag_header->tag[0]; + *out.last++ = tag->tag_header->tag[1]; + *out.last++ = tag->tag_header->tag[2]; + + *out.last++ = tag->tag_header->major_version; + *out.last++ = tag->tag_header->minor_version; + *out.last++ = tag->tag_header->flags; + *out.last++ = 0x00; + *out.last++ = 0x00; + *out.last++ = 0x00; + *out.last++ = tag->tag_header->tag_size; + + frame_list = tag->frames->start; + while(frame_list != NULL) + { + *out.last++ = frame_list->frame->frame_id[0]; + *out.last++ = frame_list->frame->frame_id[1]; + *out.last++ = frame_list->frame->frame_id[2]; + *out.last++ = frame_list->frame->frame_id[3]; + + // int enc = syncint_encode(frame_list->frame->size); + int enc = frame_list->frame->size + 1; + *out.last++ = (enc >> 24) & 0xFF; + *out.last++ = (enc >> 16) & 0xFF; + *out.last++ = (enc >> 8) & 0xFF; + *out.last++ = (enc) & 0xFF; + + //*out.last++ = frame_list->frame->flags[0]; + //*out.last++ = frame_list->frame->flags[1]; + *out.last++ = 0x00; + *out.last++ = 0x00; + + int i; + for(i=0; iframe->size; i++) + *out.last++ = frame_list->frame->data[i]; + + *out.last++ = 0x00; + frame_list = frame_list->next; + } + rc = ngx_rtmp_mpegts_write_frame(&ctx->file, &frame, &out); if (rc != NGX_OK) { From e09fdd20cef361cc323c78847d5498c816294b9d Mon Sep 17 00:00:00 2001 From: gitaudo Date: Tue, 20 Oct 2020 13:23:49 +0200 Subject: [PATCH 4/8] Added undeclared variables for cc and amf_handler --- hls/ngx_rtmp_hls_module.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 83c1d935e..bae382dcf 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -72,6 +72,7 @@ typedef struct { ngx_uint_t audio_cc; ngx_uint_t video_cc; + ngx_uint_t meta_cc; ngx_uint_t key_frags; uint64_t aframe_base; @@ -2580,6 +2581,7 @@ ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf) { ngx_rtmp_core_main_conf_t *cmcf; ngx_rtmp_handler_pt *h; + ngx_rtmp_amf_handler_t *ch; cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module); From 85fb44e737f3cd49b4f46fa409ec2bd99726f6aa Mon Sep 17 00:00:00 2001 From: gitaudo Date: Tue, 20 Oct 2020 17:29:27 +0200 Subject: [PATCH 5/8] Fixed pts and data_aligment_bit of metadata packet --- hls/ngx_rtmp_hls_module.c | 2 +- hls/ngx_rtmp_mpegts.c | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index bae382dcf..e7dab5afb 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -2488,7 +2488,7 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_memzero(&frame, sizeof(frame)); frame.cc = ctx->meta_cc; - frame.dts = (uint64_t) h->timestamp * 90; + frame.dts = (uint64_t) h->timestamp * 90 + 100; frame.pts = frame.dts; frame.pid = 0x102; frame.sid = 0xbd; diff --git a/hls/ngx_rtmp_mpegts.c b/hls/ngx_rtmp_mpegts.c index 9589c5b44..47f96c650 100644 --- a/hls/ngx_rtmp_mpegts.c +++ b/hls/ngx_rtmp_mpegts.c @@ -264,7 +264,11 @@ ngx_rtmp_mpegts_write_frame(ngx_rtmp_mpegts_file_t *file, *p++ = (u_char) (pes_size >> 8); *p++ = (u_char) pes_size; - *p++ = 0x80; /* H222 */ + if (f->pid == 0x102) { + *p++ = 0x84; /* set data aligment bit for metadata packet */ + } else { + *p++ = 0x80; /* H222 */ + } *p++ = (u_char) flags; *p++ = (u_char) header_size; From a17b6aed2ca4cd682b6099918f85beb36b1813c5 Mon Sep 17 00:00:00 2001 From: gitaudo Date: Fri, 6 Nov 2020 10:31:05 +0100 Subject: [PATCH 6/8] Added space trimming for meta strings and pipe separator support --- hls/ngx_rtmp_hls_module.c | 42 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 39 insertions(+), 3 deletions(-) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index e7dab5afb..6b266e4bd 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -2432,6 +2432,23 @@ ngx_rtmp_hls_merge_app_conf(ngx_conf_t *cf, void *parent, void *child) } +char * trim_spaces(char *str) { + char *end; + /* skip leading whitespace */ + while (isspace(*str)) { + str = str + 1; + } + /* remove trailing whitespace */ + end = str + strlen(str) - 1; + while (end > str && isspace(*end)) { + end = end - 1; + } + + *(end+1) = '\0'; + return str; +} + + static ngx_int_t ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_chain_t *in) @@ -2441,6 +2458,7 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, ngx_int_t rc; ngx_buf_t out; ngx_uint_t skip; + char *token; static u_char buffer[132]; ID3v2_tag* tag; @@ -2448,6 +2466,8 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, static struct { char title[32]; + char artist[32]; + char streamTitle[64]; } v; ngx_memzero(&v, sizeof(v)); @@ -2455,7 +2475,7 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, static ngx_rtmp_amf_elt_t in_inf[] = { { NGX_RTMP_AMF_STRING, ngx_string("StreamTitle"), - &v.title, 32 }, + &v.streamTitle, 64 }, }; static ngx_rtmp_amf_elt_t in_elts[] = { @@ -2479,6 +2499,13 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } + token = strtok(v.streamTitle, "|"); + token = trim_spaces(token); + snprintf(v.title, sizeof(v.title), "%s", token); + + token = strtok(NULL, "|"); + token = trim_spaces(token); + snprintf(v.artist, sizeof(v.artist), "%s", token); ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); @@ -2503,7 +2530,7 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, tag = new_tag(); tag_set_title(v.title, 3, tag); - // tag_set_artist("gabriele", 0, tag); + tag_set_artist(v.artist, 3, tag); tag->tag_header = new_header(); memcpy(tag->tag_header->tag, "ID3", 3); @@ -2579,7 +2606,7 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, static ngx_int_t ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf) { - ngx_rtmp_core_main_conf_t *cmcf; + ngx_rtmp_core_main_conf_t *cmcf; ngx_rtmp_handler_pt *h; ngx_rtmp_amf_handler_t *ch; @@ -2591,6 +2618,15 @@ ngx_rtmp_hls_postconfiguration(ngx_conf_t *cf) h = ngx_array_push(&cmcf->events[NGX_RTMP_MSG_AUDIO]); *h = ngx_rtmp_hls_audio; + + ch = ngx_array_push(&cmcf->amf); + if (ch == NULL) { + return NGX_ERROR; + } + ngx_str_set(&ch->name, "@setDataFrame"); + ch->handler = ngx_rtmp_hls_meta; + + ch = ngx_array_push(&cmcf->amf); if (ch == NULL) { return NGX_ERROR; From 887742a6a7a2790ebaf17163538c151fed07086b Mon Sep 17 00:00:00 2001 From: gitaudo Date: Tue, 17 Nov 2020 16:42:13 +0100 Subject: [PATCH 7/8] Fixed metadata parsing when artist not present --- hls/ngx_rtmp_hls_module.c | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index 6b266e4bd..ddc35015d 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -2471,7 +2471,7 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, } v; ngx_memzero(&v, sizeof(v)); - + static ngx_rtmp_amf_elt_t in_inf[] = { { NGX_RTMP_AMF_STRING, ngx_string("StreamTitle"), @@ -2504,9 +2504,11 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, snprintf(v.title, sizeof(v.title), "%s", token); token = strtok(NULL, "|"); - token = trim_spaces(token); - snprintf(v.artist, sizeof(v.artist), "%s", token); - + if (token != NULL) { + token = trim_spaces(token); + snprintf(v.artist, sizeof(v.artist), "%s", token); + } + ctx = ngx_rtmp_get_module_ctx(s, ngx_rtmp_hls_module); if (ctx == NULL) { @@ -2527,7 +2529,6 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } - tag = new_tag(); tag_set_title(v.title, 3, tag); tag_set_artist(v.artist, 3, tag); From 15e7fcdcfc014c98f5f646bcc68f882581a1164f Mon Sep 17 00:00:00 2001 From: gitaudo Date: Wed, 18 Nov 2020 15:03:56 +0100 Subject: [PATCH 8/8] Fixed connection interruption when no metadata provided --- hls/ngx_rtmp_hls_module.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/hls/ngx_rtmp_hls_module.c b/hls/ngx_rtmp_hls_module.c index ddc35015d..d41ebe308 100644 --- a/hls/ngx_rtmp_hls_module.c +++ b/hls/ngx_rtmp_hls_module.c @@ -2499,10 +2499,14 @@ ngx_rtmp_hls_meta(ngx_rtmp_session_t *s, ngx_rtmp_header_t *h, return NGX_OK; } - token = strtok(v.streamTitle, "|"); - token = trim_spaces(token); - snprintf(v.title, sizeof(v.title), "%s", token); - + token = strtok(v.streamTitle, "|"); + if (token != NULL) { + token = trim_spaces(token); + snprintf(v.title, sizeof(v.title), "%s", token); + } else { + return NGX_OK; + } + token = strtok(NULL, "|"); if (token != NULL) { token = trim_spaces(token);