mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2025-02-02 15:07:36 -05:00
Feature: Extract metadata and export them to stats.xml
All metadata is now extracted from Vorbis streams and ICY streams and exported as new <metadata> 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
This commit is contained in:
parent
795aa278ad
commit
28572be2ec
26
src/format.c
26
src/format.c
@ -30,6 +30,8 @@
|
||||
# include <sys/types.h>
|
||||
#endif
|
||||
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -18,6 +18,8 @@
|
||||
#ifndef __FORMAT_H__
|
||||
#define __FORMAT_H__
|
||||
|
||||
#include <vorbis/codec.h>
|
||||
|
||||
#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__ */
|
||||
|
||||
|
@ -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)
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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)
|
||||
{
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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,22 +433,10 @@ 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
|
||||
if (strcmp(tag, "song") == 0)
|
||||
tag = "title";
|
||||
|
||||
format_set_vorbiscomment(plugin, tag, value);
|
||||
free (value);
|
||||
}
|
||||
|
||||
@ -463,7 +444,7 @@ static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char
|
||||
/* 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);
|
||||
|
29
src/stats.c
29
src/stats.c
@ -19,6 +19,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h>
|
||||
#include <ctype.h>
|
||||
|
||||
#include <libxml/xmlmemory.h>
|
||||
#include <libxml/parser.h>
|
||||
@ -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);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user