mirror of
https://gitlab.xiph.org/xiph/icecast-server.git
synced 2024-09-22 04:15:54 -04: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>
|
# include <sys/types.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include <vorbis/codec.h>
|
||||||
|
|
||||||
#include "connection.h"
|
#include "connection.h"
|
||||||
#include "refbuf.h"
|
#include "refbuf.h"
|
||||||
|
|
||||||
@ -409,4 +411,28 @@ static int format_prepare_headers (source_t *source, client_t *client)
|
|||||||
return 0;
|
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__
|
#ifndef __FORMAT_H__
|
||||||
#define __FORMAT_H__
|
#define __FORMAT_H__
|
||||||
|
|
||||||
|
#include <vorbis/codec.h>
|
||||||
|
|
||||||
#include "client.h"
|
#include "client.h"
|
||||||
#include "refbuf.h"
|
#include "refbuf.h"
|
||||||
#include "common/httpp/httpp.h"
|
#include "common/httpp/httpp.h"
|
||||||
@ -53,6 +55,9 @@ typedef struct _format_plugin_tag
|
|||||||
void (*free_plugin)(struct _format_plugin_tag *self);
|
void (*free_plugin)(struct _format_plugin_tag *self);
|
||||||
void (*apply_settings)(client_t *client, struct _format_plugin_tag *format, struct _mount_proxy *mount);
|
void (*apply_settings)(client_t *client, struct _format_plugin_tag *format, struct _mount_proxy *mount);
|
||||||
|
|
||||||
|
/* meta data */
|
||||||
|
vorbis_comment vc;
|
||||||
|
|
||||||
/* for internal state management */
|
/* for internal state management */
|
||||||
void *_state;
|
void *_state;
|
||||||
} format_plugin_t;
|
} 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_http_buffer (struct source_tag *source, client_t *client);
|
||||||
int format_check_file_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,
|
void format_send_general_headers(format_plugin_t *format,
|
||||||
struct source_tag *source, client_t *client);
|
struct source_tag *source, client_t *client);
|
||||||
|
|
||||||
|
void format_set_vorbiscomment(format_plugin_t *plugin, const char *tag, const char *value);
|
||||||
|
|
||||||
#endif /* __FORMAT_H__ */
|
#endif /* __FORMAT_H__ */
|
||||||
|
|
||||||
|
@ -98,6 +98,7 @@ int format_ebml_get_plugin(source_t *source)
|
|||||||
plugin->contenttype = httpp_getvar(source->parser, "content-type");
|
plugin->contenttype = httpp_getvar(source->parser, "content-type");
|
||||||
|
|
||||||
plugin->_state = ebml_source_state;
|
plugin->_state = ebml_source_state;
|
||||||
|
vorbis_comment_init(&plugin->vc);
|
||||||
source->format = plugin;
|
source->format = plugin;
|
||||||
|
|
||||||
ebml_source_state->ebml = ebml_create();
|
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);
|
refbuf_release(ebml_source_state->header);
|
||||||
ebml_destroy(ebml_source_state->ebml);
|
ebml_destroy(ebml_source_state->ebml);
|
||||||
free(ebml_source_state);
|
free(ebml_source_state);
|
||||||
|
vorbis_comment_clear(&plugin->vc);
|
||||||
free(plugin);
|
free(plugin);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int send_ebml_header(client_t *client)
|
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 */
|
/* 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;
|
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
|
/* kate pages are not rebuilt, so here we just for headers and then
|
||||||
* pass them straight through to the the queue
|
* 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;
|
kate_codec_t *kate = codec->specific;
|
||||||
ogg_packet packet;
|
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 */
|
/* 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;
|
refbuf_t * refbuf;
|
||||||
|
|
||||||
|
@ -118,6 +118,8 @@ int format_mp3_get_plugin(source_t *source)
|
|||||||
state->interval = state->inline_metadata_interval;
|
state->interval = state->inline_metadata_interval;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vorbis_comment_init(&plugin->vc);
|
||||||
source->format = plugin;
|
source->format = plugin;
|
||||||
thread_mutex_create(&state->url_lock);
|
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);
|
value = strdup (in_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp (tag, "title") == 0 || strcmp (tag, "song") == 0)
|
if (strcmp(tag, "title") == 0 || strcmp(tag, "song") == 0) {
|
||||||
{
|
tag = MP3_METADATA_TITLE;
|
||||||
free (source_mp3->url_title);
|
} else if (strcmp(tag, "artist") == 0) {
|
||||||
source_mp3->url_title = value;
|
tag = MP3_METADATA_ARTIST;
|
||||||
|
} else if (strcmp(tag, "url") == 0) {
|
||||||
|
tag = MP3_METADATA_URL;
|
||||||
}
|
}
|
||||||
else if (strcmp (tag, "artist") == 0)
|
|
||||||
{
|
format_set_vorbiscomment(plugin, tag, value);
|
||||||
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);
|
free (value);
|
||||||
|
|
||||||
thread_mutex_unlock (&source_mp3->url_lock);
|
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 streamtitle[] = "StreamTitle='";
|
||||||
const char streamurl[] = "StreamUrl='";
|
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;
|
size_t size;
|
||||||
unsigned char len_byte;
|
unsigned char len_byte;
|
||||||
refbuf_t *p;
|
refbuf_t *p;
|
||||||
@ -249,11 +248,11 @@ static void mp3_set_title(source_t *source)
|
|||||||
thread_mutex_lock (&source_mp3->url_lock);
|
thread_mutex_lock (&source_mp3->url_lock);
|
||||||
|
|
||||||
/* work out message length */
|
/* work out message length */
|
||||||
if (source_mp3->url_artist)
|
if (url_artist)
|
||||||
len += strlen (source_mp3->url_artist);
|
len += strlen (url_artist);
|
||||||
if (source_mp3->url_title)
|
if (url_title)
|
||||||
len += strlen (source_mp3->url_title);
|
len += strlen (url_title);
|
||||||
if (source_mp3->url_artist && source_mp3->url_title)
|
if (url_artist && url_title)
|
||||||
len += 3;
|
len += 3;
|
||||||
if (source_mp3->inline_url)
|
if (source_mp3->inline_url)
|
||||||
{
|
{
|
||||||
@ -261,8 +260,8 @@ static void mp3_set_title(source_t *source)
|
|||||||
if (end)
|
if (end)
|
||||||
len += end - source_mp3->inline_url+2;
|
len += end - source_mp3->inline_url+2;
|
||||||
}
|
}
|
||||||
else if (source_mp3->url)
|
else if (url)
|
||||||
len += strlen (source_mp3->url) + strlen (streamurl) + 2;
|
len += strlen (url) + strlen (streamurl) + 2;
|
||||||
#define MAX_META_LEN 255*16
|
#define MAX_META_LEN 255*16
|
||||||
if (len > MAX_META_LEN)
|
if (len > MAX_META_LEN)
|
||||||
{
|
{
|
||||||
@ -283,12 +282,12 @@ static void mp3_set_title(source_t *source)
|
|||||||
int r;
|
int r;
|
||||||
|
|
||||||
memset (p->data, '\0', size);
|
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,
|
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
|
else
|
||||||
r = snprintf (p->data, size, "%c%s%s';", len_byte, streamtitle,
|
r = snprintf (p->data, size, "%c%s%s';", len_byte, streamtitle,
|
||||||
source_mp3->url_title);
|
url_title);
|
||||||
if (r > 0)
|
if (r > 0)
|
||||||
{
|
{
|
||||||
if (source_mp3->inline_url)
|
if (source_mp3->inline_url)
|
||||||
@ -299,8 +298,8 @@ static void mp3_set_title(source_t *source)
|
|||||||
if ((ssize_t)(size-r) > urllen)
|
if ((ssize_t)(size-r) > urllen)
|
||||||
snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->inline_url+11);
|
snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->inline_url+11);
|
||||||
}
|
}
|
||||||
else if (source_mp3->url)
|
else if (url)
|
||||||
snprintf (p->data+r, size-r, "StreamUrl='%s';", source_mp3->url);
|
snprintf (p->data+r, size-r, "StreamUrl='%s';", url);
|
||||||
}
|
}
|
||||||
ICECAST_LOG_DEBUG("shoutcast metadata block setup with %s", p->data+1);
|
ICECAST_LOG_DEBUG("shoutcast metadata block setup with %s", p->data+1);
|
||||||
filter_shoutcast_metadata (source, p->data, size);
|
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;
|
mp3_state *state = self->_state;
|
||||||
|
|
||||||
thread_mutex_destroy(&state->url_lock);
|
thread_mutex_destroy(&state->url_lock);
|
||||||
free(state->url_artist);
|
|
||||||
free(state->url_title);
|
|
||||||
free(self->charset);
|
free(self->charset);
|
||||||
refbuf_release(state->metadata);
|
refbuf_release(state->metadata);
|
||||||
refbuf_release(state->read_data);
|
refbuf_release(state->read_data);
|
||||||
free(state);
|
free(state);
|
||||||
|
vorbis_comment_clear(&self->vc);
|
||||||
free(self);
|
free(self);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -18,14 +18,15 @@
|
|||||||
#ifndef __FORMAT_MP3_H__
|
#ifndef __FORMAT_MP3_H__
|
||||||
#define __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 {
|
typedef struct {
|
||||||
/* These are for inline metadata */
|
/* These are for inline metadata */
|
||||||
int inline_metadata_interval;
|
int inline_metadata_interval;
|
||||||
int offset;
|
int offset;
|
||||||
int interval;
|
int interval;
|
||||||
char *url_artist;
|
|
||||||
char *url_title;
|
|
||||||
char *url;
|
|
||||||
char *inline_url;
|
char *inline_url;
|
||||||
int update_metadata;
|
int update_metadata;
|
||||||
|
|
||||||
|
@ -176,6 +176,7 @@ int format_ogg_get_plugin(source_t *source)
|
|||||||
plugin->contenttype = httpp_getvar (source->parser, "content-type");
|
plugin->contenttype = httpp_getvar (source->parser, "content-type");
|
||||||
|
|
||||||
ogg_sync_init (&state->oy);
|
ogg_sync_init (&state->oy);
|
||||||
|
vorbis_comment_init(&plugin->vc);
|
||||||
|
|
||||||
plugin->_state = state;
|
plugin->_state = state;
|
||||||
source->format = plugin;
|
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 memory associated with this plugin instance */
|
||||||
free_ogg_codecs (state);
|
free_ogg_codecs (state);
|
||||||
free (state->artist);
|
|
||||||
free (state->title);
|
|
||||||
|
|
||||||
ogg_sync_clear (&state->oy);
|
ogg_sync_clear (&state->oy);
|
||||||
|
|
||||||
free (state);
|
free (state);
|
||||||
|
|
||||||
|
vorbis_comment_clear(&plugin->vc);
|
||||||
|
|
||||||
free (plugin);
|
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)
|
static void update_comments(source_t *source)
|
||||||
{
|
{
|
||||||
ogg_state_t *ogg_info = source->format->_state;
|
ogg_state_t *ogg_info = source->format->_state;
|
||||||
char *title = ogg_info->title;
|
char *title = vorbis_comment_query(&source->format->vc, "TITLE", 0);
|
||||||
char *artist = ogg_info->artist;
|
char *artist = vorbis_comment_query(&source->format->vc, "ARTIST", 0);
|
||||||
char *metadata = NULL;
|
char *metadata = NULL;
|
||||||
unsigned int len = 1; /* space for the nul byte at least */
|
unsigned int len = 1; /* space for the nul byte at least */
|
||||||
ogg_codec_t *codec;
|
ogg_codec_t *codec;
|
||||||
char codec_names [100] = "";
|
char codec_names [100] = "";
|
||||||
|
|
||||||
if (ogg_info->artist)
|
if (artist)
|
||||||
{
|
{
|
||||||
if (title)
|
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
|
/* process the incoming page. this requires searching through the
|
||||||
* currently known codecs that have been seen in the stream
|
* 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;
|
ogg_codec_t *codec = ogg_info->codecs;
|
||||||
refbuf_t *refbuf = NULL;
|
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 (ogg_page_serialno (page) == codec->os.serialno)
|
||||||
{
|
{
|
||||||
if (codec->process_page)
|
if (codec->process_page)
|
||||||
refbuf = codec->process_page(ogg_info, codec, page);
|
refbuf = codec->process_page(ogg_info, codec, page, plugin);
|
||||||
break;
|
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 a codec has just been given a page then process it */
|
||||||
if (codec && codec->process)
|
if (codec && codec->process)
|
||||||
{
|
{
|
||||||
refbuf = codec->process (ogg_info, codec);
|
refbuf = codec->process (ogg_info, codec, source->format);
|
||||||
if (refbuf)
|
if (refbuf)
|
||||||
return complete_buffer (source, refbuf);
|
return complete_buffer (source, refbuf);
|
||||||
|
|
||||||
@ -426,7 +428,7 @@ static refbuf_t *ogg_get_buffer(source_t *source)
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
ogg_info->bos_completed = 1;
|
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)
|
if (ogg_info->error)
|
||||||
{
|
{
|
||||||
|
@ -30,8 +30,6 @@ typedef struct ogg_state_tag
|
|||||||
|
|
||||||
int codec_count;
|
int codec_count;
|
||||||
struct ogg_codec_tag *codecs;
|
struct ogg_codec_tag *codecs;
|
||||||
char *artist;
|
|
||||||
char *title;
|
|
||||||
int log_metadata;
|
int log_metadata;
|
||||||
refbuf_t *file_headers;
|
refbuf_t *file_headers;
|
||||||
refbuf_t *header_pages;
|
refbuf_t *header_pages;
|
||||||
@ -55,9 +53,9 @@ typedef struct ogg_codec_tag
|
|||||||
refbuf_t *possible_start;
|
refbuf_t *possible_start;
|
||||||
refbuf_t *page;
|
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,
|
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);
|
void (*codec_free)(ogg_state_t *ogg_info, struct ogg_codec_tag *codec);
|
||||||
} ogg_codec_t;
|
} 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,
|
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;
|
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
|
/* skeleton pages are not rebuilt, so here we just for headers and then
|
||||||
* pass them straight through to the the queue
|
* 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;
|
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,
|
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;
|
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
|
/* theora pages are not rebuilt, so here we just for headers and then
|
||||||
* pass them straight through to the the queue
|
* 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;
|
theora_codec_t *theora = codec->specific;
|
||||||
ogg_packet packet;
|
ogg_packet packet;
|
||||||
|
@ -38,7 +38,6 @@
|
|||||||
typedef struct vorbis_codec_tag
|
typedef struct vorbis_codec_tag
|
||||||
{
|
{
|
||||||
vorbis_info vi;
|
vorbis_info vi;
|
||||||
vorbis_comment vc;
|
|
||||||
|
|
||||||
int rebuild_comment;
|
int rebuild_comment;
|
||||||
int stream_notify;
|
int stream_notify;
|
||||||
@ -58,15 +57,15 @@ typedef struct vorbis_codec_tag
|
|||||||
ogg_packet *header[3];
|
ogg_packet *header[3];
|
||||||
ogg_int64_t prev_page_samples;
|
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);
|
refbuf_t *(*get_buffer_page)(ogg_state_t *ogg_info, ogg_codec_t *codec);
|
||||||
|
|
||||||
} vorbis_codec_t;
|
} 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,
|
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);
|
||||||
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);
|
||||||
static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char *value, const char *charset);
|
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_channels", NULL);
|
||||||
stats_event (ogg_info->mount, "audio_samplerate", NULL);
|
stats_event (ogg_info->mount, "audio_samplerate", NULL);
|
||||||
vorbis_info_clear (&vorbis->vi);
|
vorbis_info_clear (&vorbis->vi);
|
||||||
vorbis_comment_clear (&vorbis->vc);
|
|
||||||
ogg_stream_clear (&codec->os);
|
ogg_stream_clear (&codec->os);
|
||||||
ogg_stream_clear (&vorbis->new_os);
|
ogg_stream_clear (&vorbis->new_os);
|
||||||
free_ogg_packet (vorbis->header[0]);
|
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
|
* 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
|
* 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;
|
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
|
/* This handles the headers at the backend, here we insert the header packets
|
||||||
* we want for the queue.
|
* 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;
|
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 */
|
/* NOTE: we could build a separate comment packet each time */
|
||||||
if (source_vorbis->rebuild_comment)
|
if (source_vorbis->rebuild_comment)
|
||||||
{
|
{
|
||||||
vorbis_comment vc;
|
|
||||||
ogg_packet header;
|
ogg_packet header;
|
||||||
ice_config_t *config;
|
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();
|
config = config_get_config();
|
||||||
vorbis_comment_add_tag (&vc, "server", config->server_id);
|
format_set_vorbiscomment(plugin, "server", config->server_id);
|
||||||
config_release_config();
|
config_release_config();
|
||||||
vorbis_commentheader_out (&vc, &header);
|
vorbis_commentheader_out (&plugin->vc, &header);
|
||||||
|
|
||||||
ogg_stream_packetin (&source_vorbis->new_os, &header);
|
ogg_stream_packetin (&source_vorbis->new_os, &header);
|
||||||
vorbis_comment_clear (&vc);
|
|
||||||
ogg_packet_clear (&header);
|
ogg_packet_clear (&header);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -369,16 +360,18 @@ ogg_codec_t *initial_vorbis_page (format_plugin_t *plugin, ogg_page *page)
|
|||||||
ogg_stream_pagein (&codec->os, page);
|
ogg_stream_pagein (&codec->os, page);
|
||||||
|
|
||||||
vorbis_info_init (&vorbis->vi);
|
vorbis_info_init (&vorbis->vi);
|
||||||
vorbis_comment_init (&vorbis->vc);
|
|
||||||
|
|
||||||
ogg_stream_packetout (&codec->os, &packet);
|
ogg_stream_packetout (&codec->os, &packet);
|
||||||
|
|
||||||
|
vorbis_comment_clear(&plugin->vc);
|
||||||
|
vorbis_comment_init(&plugin->vc);
|
||||||
|
|
||||||
ICECAST_LOG_DEBUG("checking for vorbis codec");
|
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);
|
ogg_stream_clear (&codec->os);
|
||||||
vorbis_info_clear (&vorbis->vi);
|
vorbis_info_clear (&vorbis->vi);
|
||||||
vorbis_comment_clear (&vorbis->vc);
|
vorbis_comment_clear (&plugin->vc);
|
||||||
free (vorbis);
|
free (vorbis);
|
||||||
free (codec);
|
free (codec);
|
||||||
return NULL;
|
return NULL;
|
||||||
@ -440,22 +433,10 @@ static void vorbis_set_tag (format_plugin_t *plugin, const char *tag, const char
|
|||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
value = strdup (in_value);
|
value = strdup (in_value);
|
||||||
|
|
||||||
if (strcmp (tag, "artist") == 0)
|
if (strcmp(tag, "song") == 0)
|
||||||
{
|
tag = "title";
|
||||||
free (ogg_info->artist);
|
|
||||||
ogg_info->artist = value;
|
format_set_vorbiscomment(plugin, tag, 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);
|
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
|
/* 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.
|
* 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;
|
vorbis_codec_t *source_vorbis = codec->specific;
|
||||||
refbuf_t *refbuf;
|
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 &&
|
if (source_vorbis->process_packet &&
|
||||||
source_vorbis->process_packet (ogg_info, codec) > 0)
|
source_vorbis->process_packet (ogg_info, codec, plugin) > 0)
|
||||||
continue;
|
continue;
|
||||||
return NULL;
|
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
|
* back for adding to the queue
|
||||||
*/
|
*/
|
||||||
static refbuf_t *process_vorbis_passthru_page (ogg_state_t *ogg_info,
|
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);
|
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
|
* add all pages from the stream before processing packets
|
||||||
*/
|
*/
|
||||||
static refbuf_t *process_vorbis_page (ogg_state_t *ogg_info,
|
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;
|
ogg_packet header;
|
||||||
vorbis_codec_t *source_vorbis = codec->specific;
|
vorbis_codec_t *source_vorbis = codec->specific;
|
||||||
char *comment;
|
|
||||||
|
|
||||||
if (ogg_stream_pagein (&codec->os, page) < 0)
|
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;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* change comments here if need be */
|
if (vorbis_synthesis_headerin (&source_vorbis->vi, &plugin->vc, &header) < 0)
|
||||||
if (vorbis_synthesis_headerin (&source_vorbis->vi, &source_vorbis->vc, &header) < 0)
|
|
||||||
{
|
{
|
||||||
ogg_info->error = 1;
|
ogg_info->error = 1;
|
||||||
ICECAST_LOG_WARN("Problem parsing ogg vorbis header");
|
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;
|
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;
|
ogg_info->log_metadata = 1;
|
||||||
|
|
||||||
stats_event_args (ogg_info->mount, "audio_samplerate", "%ld", (long)source_vorbis->vi.rate);
|
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 <string.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdarg.h>
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
#include <libxml/xmlmemory.h>
|
#include <libxml/xmlmemory.h>
|
||||||
#include <libxml/parser.h>
|
#include <libxml/parser.h>
|
||||||
@ -985,6 +986,29 @@ void stats_transform_xslt(client_t *client, const char *uri)
|
|||||||
free(xslpath);
|
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 stats_get_xml(int show_hidden, const char *show_mount)
|
||||||
{
|
{
|
||||||
xmlDocPtr doc;
|
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);
|
node = _dump_stats_to_doc (node, show_mount, show_hidden);
|
||||||
|
|
||||||
if (show_mount && node) {
|
if (show_mount && node) {
|
||||||
|
xmlNodePtr metadata = xmlNewTextChild(node, NULL, XMLSTR("metadata"), NULL);
|
||||||
|
int i;
|
||||||
|
|
||||||
avl_tree_rlock(global.source_tree);
|
avl_tree_rlock(global.source_tree);
|
||||||
source = source_find_mount_raw(show_mount);
|
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);
|
admin_add_listeners_to_mount(source, node);
|
||||||
avl_tree_unlock(global.source_tree);
|
avl_tree_unlock(global.source_tree);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user