Skip to content

Integration of Timed Metadata for HLS #1584

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
198 changes: 197 additions & 1 deletion hls/ngx_rtmp_hls_module.c
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <ngx_rtmp_cmd_module.h>
#include <ngx_rtmp_codec_module.h>
#include "ngx_rtmp_mpegts.h"
#include "ngx_rtmp_amf.h"
#include "id3v2lib.h"


static ngx_rtmp_publish_pt next_publish;
Expand Down Expand Up @@ -70,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;
Expand Down Expand Up @@ -2429,11 +2432,188 @@ 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)
{
ngx_rtmp_hls_ctx_t *ctx;
ngx_rtmp_mpegts_frame_t frame;
ngx_int_t rc;
ngx_buf_t out;
ngx_uint_t skip;
char *token;

static u_char buffer[132];
ID3v2_tag* tag;
ID3v2_frame_list* frame_list;

static struct {
char title[32];
char artist[32];
char streamTitle[64];
} v;

ngx_memzero(&v, sizeof(v));

static ngx_rtmp_amf_elt_t in_inf[] = {
{ NGX_RTMP_AMF_STRING,
ngx_string("StreamTitle"),
&v.streamTitle, 64 },
};

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;
}

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);
snprintf(v.artist, sizeof(v.artist), "%s", token);
}

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 + 100;
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;
}

tag = new_tag();
tag_set_title(v.title, 3, tag);
tag_set_artist(v.artist, 3, 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;
out.end = buffer + sizeof(buffer);
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; i<frame_list->frame->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) {
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)
{
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;

cmcf = ngx_rtmp_conf_get_module_main_conf(cf, ngx_rtmp_core_module);

Expand All @@ -2443,6 +2623,22 @@ 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;
}
ngx_str_set(&ch->name, "onMetaData");
ch->handler = ngx_rtmp_hls_meta;

next_publish = ngx_rtmp_publish;
ngx_rtmp_publish = ngx_rtmp_hls_publish;

Expand Down
28 changes: 18 additions & 10 deletions hls/ngx_rtmp_mpegts.c
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
};


Expand Down Expand Up @@ -260,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;

Expand Down