From 28572be2ec507581a53d6aafa694b482bab041a7 Mon Sep 17 00:00:00 2001 From: Philipp Schafft Date: Tue, 9 Dec 2014 16:08:27 +0000 Subject: [PATCH] Feature: Extract metadata and export them to stats.xml All metadata is now extracted from Vorbis streams and ICY streams and exported as new tag into admin/stats if specific mountpoint is requested. To testers: - Please test with Ogg Vorbis streams. - Please test with Ogg Vorbis + Theora streams. - Please test chained Ogg streams. - Please test with ICY streams. close #2106 --- src/format.c | 26 ++++++++++++++ src/format.h | 8 +++++ src/format_ebml.c | 3 +- src/format_flac.c | 2 +- src/format_kate.c | 2 +- src/format_midi.c | 2 +- src/format_mp3.c | 58 +++++++++++++++---------------- src/format_mp3.h | 7 ++-- src/format_ogg.c | 20 ++++++----- src/format_ogg.h | 6 ++-- src/format_opus.c | 2 +- src/format_skeleton.c | 2 +- src/format_speex.c | 2 +- src/format_theora.c | 2 +- src/format_vorbis.c | 80 +++++++++++++------------------------------ src/stats.c | 29 ++++++++++++++++ 16 files changed, 140 insertions(+), 111 deletions(-) diff --git a/src/format.c b/src/format.c index b00abbfc..d745d63e 100644 --- a/src/format.c +++ b/src/format.c @@ -30,6 +30,8 @@ # include #endif +#include + #include "connection.h" #include "refbuf.h" @@ -409,4 +411,28 @@ static int format_prepare_headers (source_t *source, client_t *client) return 0; } +void format_set_vorbiscomment(format_plugin_t *plugin, const char *tag, const char *value) { + if (vorbis_comment_query_count(&plugin->vc, tag) != 0) { + /* delete key */ + /* as libvorbis hides away all the memory functions we need to copy + * the structure comment by comment. sorry about that... + */ + vorbis_comment vc; + int i; /* why does vorbis_comment use int, not size_t? */ + size_t keylen = strlen(tag); + vorbis_comment_init(&vc); + /* copy tags */ + for (i = 0; i < plugin->vc.comments; i++) { + if (strncasecmp(plugin->vc.user_comments[i], tag, keylen) == 0 && plugin->vc.user_comments[i][keylen] == '=') + continue; + vorbis_comment_add(&vc, plugin->vc.user_comments[i]); + } + /* move vendor */ + vc.vendor = plugin->vc.vendor; + plugin->vc.vendor = NULL; + vorbis_comment_clear(&plugin->vc); + plugin->vc = vc; + } + vorbis_comment_add_tag(&plugin->vc, tag, value); +} diff --git a/src/format.h b/src/format.h index 91d9b9bc..a5733644 100644 --- a/src/format.h +++ b/src/format.h @@ -18,6 +18,8 @@ #ifndef __FORMAT_H__ #define __FORMAT_H__ +#include + #include "client.h" #include "refbuf.h" #include "common/httpp/httpp.h" @@ -53,6 +55,9 @@ typedef struct _format_plugin_tag void (*free_plugin)(struct _format_plugin_tag *self); void (*apply_settings)(client_t *client, struct _format_plugin_tag *format, struct _mount_proxy *mount); + /* meta data */ + vorbis_comment vc; + /* for internal state management */ void *_state; } format_plugin_t; @@ -66,8 +71,11 @@ int format_advance_queue (struct source_tag *source, client_t *client); int format_check_http_buffer (struct source_tag *source, client_t *client); int format_check_file_buffer (struct source_tag *source, client_t *client); + void format_send_general_headers(format_plugin_t *format, struct source_tag *source, client_t *client); +void format_set_vorbiscomment(format_plugin_t *plugin, const char *tag, const char *value); + #endif /* __FORMAT_H__ */ diff --git a/src/format_ebml.c b/src/format_ebml.c index 4b57c551..4c936407 100644 --- a/src/format_ebml.c +++ b/src/format_ebml.c @@ -98,6 +98,7 @@ int format_ebml_get_plugin(source_t *source) plugin->contenttype = httpp_getvar(source->parser, "content-type"); plugin->_state = ebml_source_state; + vorbis_comment_init(&plugin->vc); source->format = plugin; ebml_source_state->ebml = ebml_create(); @@ -112,8 +113,8 @@ static void ebml_free_plugin(format_plugin_t *plugin) refbuf_release(ebml_source_state->header); ebml_destroy(ebml_source_state->ebml); free(ebml_source_state); + vorbis_comment_clear(&plugin->vc); free(plugin); - } static int send_ebml_header(client_t *client) diff --git a/src/format_flac.c b/src/format_flac.c index 9eae1e77..7c56233d 100644 --- a/src/format_flac.c +++ b/src/format_flac.c @@ -42,7 +42,7 @@ static void flac_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) /* Here, we just verify the page is ok and then add it to the queue */ -static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) +static refbuf_t *process_flac_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { refbuf_t * refbuf; diff --git a/src/format_kate.c b/src/format_kate.c index 6d069d7a..a5211183 100644 --- a/src/format_kate.c +++ b/src/format_kate.c @@ -74,7 +74,7 @@ static void kate_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) /* kate pages are not rebuilt, so here we just for headers and then * pass them straight through to the the queue */ -static refbuf_t *process_kate_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) +static refbuf_t *process_kate_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { kate_codec_t *kate = codec->specific; ogg_packet packet; diff --git a/src/format_midi.c b/src/format_midi.c index ff5ad804..6ffa7f26 100644 --- a/src/format_midi.c +++ b/src/format_midi.c @@ -41,7 +41,7 @@ static void midi_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) /* Here, we just verify the page is ok and then add it to the queue */ -static refbuf_t *process_midi_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) +static refbuf_t *process_midi_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { refbuf_t * refbuf; diff --git a/src/format_mp3.c b/src/format_mp3.c index 941249e9..13ae32da 100644 --- a/src/format_mp3.c +++ b/src/format_mp3.c @@ -118,6 +118,8 @@ int format_mp3_get_plugin(source_t *source) state->interval = state->inline_metadata_interval; } } + + vorbis_comment_init(&plugin->vc); source->format = plugin; thread_mutex_create(&state->url_lock); @@ -147,23 +149,17 @@ static void mp3_set_tag (format_plugin_t *plugin, const char *tag, const char *i value = strdup (in_value); } - if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0) - { - free (source_mp3->url_title); - source_mp3->url_title = value; + if (strcmp(tag, "title") == 0 || strcmp(tag, "song") == 0) { + tag = MP3_METADATA_TITLE; + } else if (strcmp(tag, "artist") == 0) { + tag = MP3_METADATA_ARTIST; + } else if (strcmp(tag, "url") == 0) { + tag = MP3_METADATA_URL; } - else if (strcmp (tag, "artist") == 0) - { - free (source_mp3->url_artist); - source_mp3->url_artist = value; - } - else if (strcmp (tag, "url") == 0) - { - free (source_mp3->url); - source_mp3->url = value; - } - else - free (value); + + format_set_vorbiscomment(plugin, tag, value); + free (value); + thread_mutex_unlock (&source_mp3->url_lock); } @@ -239,6 +235,9 @@ static void mp3_set_title(source_t *source) { const char streamtitle[] = "StreamTitle='"; const char streamurl[] = "StreamUrl='"; + const char *url_artist = vorbis_comment_query(&source->format->vc, MP3_METADATA_ARTIST, 0); + const char *url_title = vorbis_comment_query(&source->format->vc, MP3_METADATA_TITLE, 0); + const char *url = vorbis_comment_query(&source->format->vc, MP3_METADATA_URL, 0); size_t size; unsigned char len_byte; refbuf_t *p; @@ -249,11 +248,11 @@ static void mp3_set_title(source_t *source) thread_mutex_lock (&source_mp3->url_lock); /* work out message length */ - if (source_mp3->url_artist) - len += strlen (source_mp3->url_artist); - if (source_mp3->url_title) - len += strlen (source_mp3->url_title); - if (source_mp3->url_artist && source_mp3->url_title) + if (url_artist) + len += strlen (url_artist); + if (url_title) + len += strlen (url_title); + if (url_artist && url_title) len += 3; if (source_mp3->inline_url) { @@ -261,8 +260,8 @@ static void mp3_set_title(source_t *source) if (end) len += end - source_mp3->inline_url+2; } - else if (source_mp3->url) - len += strlen (source_mp3->url) + strlen (streamurl) + 2; + else if (url) + len += strlen (url) + strlen (streamurl) + 2; #define MAX_META_LEN 255*16 if (len > MAX_META_LEN) { @@ -283,12 +282,12 @@ static void mp3_set_title(source_t *source) int r; memset (p->data, '\0', size); - if (source_mp3->url_artist && source_mp3->url_title) + if (url_artist && url_title) r = snprintf (p->data, size, "%c%s%s - %s';", len_byte, streamtitle, - source_mp3->url_artist, source_mp3->url_title); + url_artist, url_title); else r = snprintf (p->data, size, "%c%s%s';", len_byte, streamtitle, - source_mp3->url_title); + url_title); if (r > 0) { if (source_mp3->inline_url) @@ -299,8 +298,8 @@ static void mp3_set_title(source_t *source) if ((ssize_t)(size-r) > urllen) snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->inline_url+11); } - else if (source_mp3->url) - snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->url); + else if (url) + snprintf (p->data+r, size-r, "StreamUrl='%s';", url); } ICECAST_LOG_DEBUG("shoutcast metadata block setup with %s", p->data+1); filter_shoutcast_metadata (source, p->data, size); @@ -450,12 +449,11 @@ static void format_mp3_free_plugin(format_plugin_t *self) mp3_state *state = self->_state; thread_mutex_destroy(&state->url_lock); - free(state->url_artist); - free(state->url_title); free(self->charset); refbuf_release(state->metadata); refbuf_release(state->read_data); free(state); + vorbis_comment_clear(&self->vc); free(self); } diff --git a/src/format_mp3.h b/src/format_mp3.h index ff4cb365..ede5729b 100644 --- a/src/format_mp3.h +++ b/src/format_mp3.h @@ -18,14 +18,15 @@ #ifndef __FORMAT_MP3_H__ #define __FORMAT_MP3_H__ +#define MP3_METADATA_TITLE "X_ICY_TITLE" +#define MP3_METADATA_ARTIST "X_ICY_ARTIST" +#define MP3_METADATA_URL "X_ICY_URL" + typedef struct { /* These are for inline metadata */ int inline_metadata_interval; int offset; int interval; - char *url_artist; - char *url_title; - char *url; char *inline_url; int update_metadata; diff --git a/src/format_ogg.c b/src/format_ogg.c index 9ea351db..a8e72024 100644 --- a/src/format_ogg.c +++ b/src/format_ogg.c @@ -176,6 +176,7 @@ int format_ogg_get_plugin(source_t *source) plugin->contenttype = httpp_getvar (source->parser, "content-type"); ogg_sync_init (&state->oy); + vorbis_comment_init(&plugin->vc); plugin->_state = state; source->format = plugin; @@ -192,12 +193,13 @@ static void format_ogg_free_plugin (format_plugin_t *plugin) /* free memory associated with this plugin instance */ free_ogg_codecs (state); - free (state->artist); - free (state->title); ogg_sync_clear (&state->oy); + free (state); + vorbis_comment_clear(&plugin->vc); + free (plugin); } @@ -277,14 +279,14 @@ static int process_initial_page (format_plugin_t *plugin, ogg_page *page) static void update_comments(source_t *source) { ogg_state_t *ogg_info = source->format->_state; - char *title = ogg_info->title; - char *artist = ogg_info->artist; + char *title = vorbis_comment_query(&source->format->vc, "TITLE", 0); + char *artist = vorbis_comment_query(&source->format->vc, "ARTIST", 0); char *metadata = NULL; unsigned int len = 1; /* space for the nul byte at least */ ogg_codec_t *codec; char codec_names [100] = ""; - if (ogg_info->artist) + if (artist) { if (title) { @@ -367,7 +369,7 @@ static refbuf_t *complete_buffer (source_t *source, refbuf_t *refbuf) /* process the incoming page. this requires searching through the * currently known codecs that have been seen in the stream */ -static refbuf_t *process_ogg_page(ogg_state_t *ogg_info, ogg_page *page) +static refbuf_t *process_ogg_page(ogg_state_t *ogg_info, ogg_page *page, format_plugin_t *plugin) { ogg_codec_t *codec = ogg_info->codecs; refbuf_t *refbuf = NULL; @@ -377,7 +379,7 @@ static refbuf_t *process_ogg_page(ogg_state_t *ogg_info, ogg_page *page) if (ogg_page_serialno (page) == codec->os.serialno) { if (codec->process_page) - refbuf = codec->process_page(ogg_info, codec, page); + refbuf = codec->process_page(ogg_info, codec, page, plugin); break; } @@ -410,7 +412,7 @@ static refbuf_t *ogg_get_buffer(source_t *source) /* if a codec has just been given a page then process it */ if (codec && codec->process) { - refbuf = codec->process (ogg_info, codec); + refbuf = codec->process (ogg_info, codec, source->format); if (refbuf) return complete_buffer (source, refbuf); @@ -426,7 +428,7 @@ static refbuf_t *ogg_get_buffer(source_t *source) else { ogg_info->bos_completed = 1; - refbuf = process_ogg_page (ogg_info, &page); + refbuf = process_ogg_page (ogg_info, &page, source->format); } if (ogg_info->error) { diff --git a/src/format_ogg.h b/src/format_ogg.h index 5f87e7ed..04bdcf4a 100644 --- a/src/format_ogg.h +++ b/src/format_ogg.h @@ -30,8 +30,6 @@ typedef struct ogg_state_tag int codec_count; struct ogg_codec_tag *codecs; - char *artist; - char *title; int log_metadata; refbuf_t *file_headers; refbuf_t *header_pages; @@ -55,9 +53,9 @@ typedef struct ogg_codec_tag refbuf_t *possible_start; refbuf_t *page; - refbuf_t *(*process)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec); + refbuf_t *(*process)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec, format_plugin_t *plugin); refbuf_t *(*process_page)(ogg_state_t *ogg_info, - struct ogg_codec_tag *codec, ogg_page *page); + struct ogg_codec_tag *codec, ogg_page *page, format_plugin_t *plugin); void (*codec_free)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec); } ogg_codec_t; diff --git a/src/format_opus.c b/src/format_opus.c index 26eea8b6..0b606984 100644 --- a/src/format_opus.c +++ b/src/format_opus.c @@ -37,7 +37,7 @@ static void opus_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) static refbuf_t *process_opus_page (ogg_state_t *ogg_info, - ogg_codec_t *codec, ogg_page *page) + ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { refbuf_t *refbuf; diff --git a/src/format_skeleton.c b/src/format_skeleton.c index 8460df17..991ebfcc 100644 --- a/src/format_skeleton.c +++ b/src/format_skeleton.c @@ -44,7 +44,7 @@ static void skeleton_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) /* skeleton pages are not rebuilt, so here we just for headers and then * pass them straight through to the the queue */ -static refbuf_t *process_skeleton_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) +static refbuf_t *process_skeleton_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { ogg_packet packet; diff --git a/src/format_speex.c b/src/format_speex.c index bfb21fd8..e8b39bd8 100644 --- a/src/format_speex.c +++ b/src/format_speex.c @@ -38,7 +38,7 @@ static void speex_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) static refbuf_t *process_speex_page (ogg_state_t *ogg_info, - ogg_codec_t *codec, ogg_page *page) + ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { refbuf_t *refbuf; diff --git a/src/format_theora.c b/src/format_theora.c index cd30c9dc..b968c601 100644 --- a/src/format_theora.c +++ b/src/format_theora.c @@ -63,7 +63,7 @@ static void theora_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) /* theora pages are not rebuilt, so here we just for headers and then * pass them straight through to the the queue */ -static refbuf_t *process_theora_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page) +static refbuf_t *process_theora_page (ogg_state_t *ogg_info, ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { theora_codec_t *theora = codec->specific; ogg_packet packet; diff --git a/src/format_vorbis.c b/src/format_vorbis.c index 544fd117..c68ec5a7 100644 --- a/src/format_vorbis.c +++ b/src/format_vorbis.c @@ -38,7 +38,6 @@ typedef struct vorbis_codec_tag { vorbis_info vi; - vorbis_comment vc; int rebuild_comment; int stream_notify; @@ -58,15 +57,15 @@ typedef struct vorbis_codec_tag ogg_packet *header[3]; ogg_int64_t prev_page_samples; - int (*process_packet)(ogg_state_t *ogg_info, ogg_codec_t *codec); + int (*process_packet)(ogg_state_t *ogg_info, ogg_codec_t *codec, format_plugin_t *plugin); refbuf_t *(*get_buffer_page)(ogg_state_t *ogg_info, ogg_codec_t *codec); } vorbis_codec_t; -static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec); +static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec, format_plugin_t *plugin); static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info, - ogg_codec_t *codec, ogg_page *page); -static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec); + ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin); +static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec, format_plugin_t *plugin); static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char *value, const char *charset); @@ -89,7 +88,6 @@ static void vorbis_codec_free (ogg_state_t *ogg_info, ogg_codec_t *codec) stats_event (ogg_info->mount, "audio_channels", NULL); stats_event (ogg_info->mount, "audio_samplerate", NULL); vorbis_info_clear (&vorbis->vi); - vorbis_comment_clear (&vorbis->vc); ogg_stream_clear (&codec->os); ogg_stream_clear (&vorbis->new_os); free_ogg_packet (vorbis->header[0]); @@ -235,7 +233,7 @@ static void initiate_flush (vorbis_codec_t *source_vorbis) * and add them into the new stream, flushing after so many samples. We * also check if an new headers are requested after each processed page */ -static int process_vorbis_audio(ogg_state_t *ogg_info, ogg_codec_t *codec) +static int process_vorbis_audio(ogg_state_t *ogg_info, ogg_codec_t *codec, format_plugin_t *plugin) { vorbis_codec_t *source_vorbis = codec->specific; @@ -311,7 +309,7 @@ static int process_vorbis_audio(ogg_state_t *ogg_info, ogg_codec_t *codec) /* This handles the headers at the backend, here we insert the header packets * we want for the queue. */ -static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec) +static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec, format_plugin_t *plugin) { vorbis_codec_t *source_vorbis = codec->specific; @@ -323,22 +321,15 @@ static int process_vorbis_headers (ogg_state_t *ogg_info, ogg_codec_t *codec) /* NOTE: we could build a separate comment packet each time */ if (source_vorbis->rebuild_comment) { - vorbis_comment vc; ogg_packet header; ice_config_t *config; - vorbis_comment_init (&vc); - if (ogg_info->artist) - vorbis_comment_add_tag (&vc, "artist", ogg_info->artist); - if (ogg_info->title) - vorbis_comment_add_tag (&vc, "title", ogg_info->title); config = config_get_config(); - vorbis_comment_add_tag (&vc, "server", config->server_id); + format_set_vorbiscomment(plugin, "server", config->server_id); config_release_config(); - vorbis_commentheader_out (&vc, &header); + vorbis_commentheader_out (&plugin->vc, &header); ogg_stream_packetin (&source_vorbis->new_os, &header); - vorbis_comment_clear (&vc); ogg_packet_clear (&header); } else @@ -369,16 +360,18 @@ ogg_codec_t *initial_vorbis_page (format_plugin_t *plugin, ogg_page *page) ogg_stream_pagein (&codec->os, page); vorbis_info_init (&vorbis->vi); - vorbis_comment_init (&vorbis->vc); ogg_stream_packetout (&codec->os, &packet); + vorbis_comment_clear(&plugin->vc); + vorbis_comment_init(&plugin->vc); + ICECAST_LOG_DEBUG("checking for vorbis codec"); - if (vorbis_synthesis_headerin (&vorbis->vi, &vorbis->vc, &packet) < 0) + if (vorbis_synthesis_headerin (&vorbis->vi, &plugin->vc, &packet) < 0) { ogg_stream_clear (&codec->os); vorbis_info_clear (&vorbis->vi); - vorbis_comment_clear (&vorbis->vc); + vorbis_comment_clear (&plugin->vc); free (vorbis); free (codec); return NULL; @@ -440,30 +433,18 @@ static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char if (value == NULL) value = strdup (in_value); - if (strcmp (tag, "artist") == 0) - { - free (ogg_info->artist); - ogg_info->artist = value; - } - else if (strcmp (tag, "title") == 0) - { - free (ogg_info->title); - ogg_info->title = value; - } - else if (strcmp (tag, "song") == 0) - { - free (ogg_info->title); - ogg_info->title = value; - } - else - free (value); + if (strcmp(tag, "song") == 0) + tag = "title"; + + format_set_vorbiscomment(plugin, tag, value); + free (value); } /* main backend routine when rebuilding streams. Here we loop until we either * have a refbuf to add onto the queue, or we want more data to process. */ -static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec) +static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec, format_plugin_t *plugin) { vorbis_codec_t *source_vorbis = codec->specific; refbuf_t *refbuf; @@ -478,7 +459,7 @@ static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec) } if (source_vorbis->process_packet && - source_vorbis->process_packet (ogg_info, codec) > 0) + source_vorbis->process_packet (ogg_info, codec, plugin) > 0) continue; return NULL; } @@ -489,7 +470,7 @@ static refbuf_t *process_vorbis (ogg_state_t *ogg_info, ogg_codec_t *codec) * back for adding to the queue */ static refbuf_t *process_vorbis_passthru_page (ogg_state_t *ogg_info, - ogg_codec_t *codec, ogg_page *page) + ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { return make_refbuf_with_page (page); } @@ -499,11 +480,10 @@ static refbuf_t *process_vorbis_passthru_page (ogg_state_t *ogg_info, * add all pages from the stream before processing packets */ static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info, - ogg_codec_t *codec, ogg_page *page) + ogg_codec_t *codec, ogg_page *page, format_plugin_t *plugin) { ogg_packet header; vorbis_codec_t *source_vorbis = codec->specific; - char *comment; if (ogg_stream_pagein (&codec->os, page) < 0) { @@ -532,8 +512,7 @@ static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info, return NULL; } - /* change comments here if need be */ - if (vorbis_synthesis_headerin (&source_vorbis->vi, &source_vorbis->vc, &header) < 0) + if (vorbis_synthesis_headerin (&source_vorbis->vi, &plugin->vc, &header) < 0) { ogg_info->error = 1; ICECAST_LOG_WARN("Problem parsing ogg vorbis header"); @@ -560,19 +539,6 @@ static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info, codec->process_page = process_vorbis_passthru_page; } - free (ogg_info->title); - comment = vorbis_comment_query (&source_vorbis->vc, "TITLE", 0); - if (comment) - ogg_info->title = strdup (comment); - else - ogg_info->title = NULL; - - free (ogg_info->artist); - comment = vorbis_comment_query (&source_vorbis->vc, "ARTIST", 0); - if (comment) - ogg_info->artist = strdup (comment); - else - ogg_info->artist = NULL; ogg_info->log_metadata = 1; stats_event_args (ogg_info->mount, "audio_samplerate", "%ld", (long)source_vorbis->vi.rate); diff --git a/src/stats.c b/src/stats.c index 747d0ade..68fa9a4f 100644 --- a/src/stats.c +++ b/src/stats.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -985,6 +986,29 @@ void stats_transform_xslt(client_t *client, const char *uri) free(xslpath); } +static void __add_metadata(xmlNodePtr node, const char *tag) { + const char *value = strstr(tag, "="); + char *name = NULL; + size_t namelen = value - tag + 1; + size_t i; + + if (!value) + return; + + name = malloc(namelen); + if (!name) + return; + + for (i = 0; i < (namelen - 1); i++) + name[i] = tolower(tag[i]); + + name[namelen-1] = 0; + + xmlNewTextChild(node, NULL, XMLSTR(name), XMLSTR(value+1)); + + free(name); +} + xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount) { xmlDocPtr doc; @@ -998,8 +1022,13 @@ xmlDocPtr stats_get_xml(int show_hidden, const char *show_mount) node = _dump_stats_to_doc (node, show_mount, show_hidden); if (show_mount && node) { + xmlNodePtr metadata = xmlNewTextChild(node, NULL, XMLSTR("metadata"), NULL); + int i; + avl_tree_rlock(global.source_tree); source = source_find_mount_raw(show_mount); + for (i = 0; i < source->format->vc.comments; i++) + __add_metadata(metadata, source->format->vc.user_comments[i]); admin_add_listeners_to_mount(source, node); avl_tree_unlock(global.source_tree); }