mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2025-01-03 14:56:35 -05:00
ezstream.c/libshout untangle 1/2
This commit is contained in:
parent
1fe0ab595c
commit
a9728c54be
254
src/ezstream.c
254
src/ezstream.c
@ -75,20 +75,16 @@ typedef struct tag_ID3Tag {
|
||||
} ID3Tag;
|
||||
|
||||
int urlParse(const char *, char **, unsigned short *, char **);
|
||||
char * shellQuote(const char *);
|
||||
char * replaceString(const char *, const char *, const char *);
|
||||
char * buildReencodeCommand(const char *, const char *, metadata_t *);
|
||||
char * getMetadataString(const char *, metadata_t *);
|
||||
metadata_t * getMetadata(const char *);
|
||||
int setMetadata(shout_t *, metadata_t *, char **);
|
||||
FILE * openResource(shout_t *, const char *, int *, metadata_t **,
|
||||
char * buildReencodeCommand(const char *, const char *, metadata_t);
|
||||
metadata_t getMetadata(const char *);
|
||||
FILE * openResource(stream_t, const char *, int *, metadata_t *,
|
||||
int *, long *);
|
||||
int reconnectServer(shout_t *, int);
|
||||
const char * getTimeString(long);
|
||||
int sendStream(shout_t *, FILE *, const char *, int, const char *,
|
||||
int sendStream(stream_t, FILE *, const char *, int, const char *,
|
||||
struct timespec *);
|
||||
int streamFile(shout_t *, const char *);
|
||||
int streamPlaylist(shout_t *);
|
||||
int streamFile(stream_t, const char *);
|
||||
int streamPlaylist(stream_t);
|
||||
int ez_shutdown(int);
|
||||
|
||||
#ifdef HAVE_SIGNALS
|
||||
@ -173,72 +169,9 @@ urlParse(const char *url, char **hostname, unsigned short *port,
|
||||
return (1);
|
||||
}
|
||||
|
||||
#define SHELLQUOTE_INLEN_MAX 8191UL
|
||||
|
||||
char *
|
||||
shellQuote(const char *in)
|
||||
{
|
||||
char *out, *out_p;
|
||||
size_t out_len;
|
||||
const char *in_p;
|
||||
|
||||
out_len = (strlen(in) > SHELLQUOTE_INLEN_MAX)
|
||||
? SHELLQUOTE_INLEN_MAX
|
||||
: strlen(in);
|
||||
out_len = out_len * 2 + 2;
|
||||
out = xcalloc(out_len + 1, sizeof(char));
|
||||
|
||||
out_p = out;
|
||||
in_p = in;
|
||||
|
||||
*out_p++ = '\'';
|
||||
out_len--;
|
||||
while (*in_p && out_len > 2) {
|
||||
switch (*in_p) {
|
||||
case '\'':
|
||||
case '\\':
|
||||
*out_p++ = '\\';
|
||||
out_len--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*out_p++ = *in_p++;
|
||||
out_len--;
|
||||
}
|
||||
*out_p++ = '\'';
|
||||
|
||||
return (out);
|
||||
}
|
||||
|
||||
char *
|
||||
replaceString(const char *source, const char *from, const char *to)
|
||||
{
|
||||
char *to_quoted, *dest;
|
||||
size_t dest_size;
|
||||
const char *p1, *p2;
|
||||
|
||||
to_quoted = shellQuote(to);
|
||||
dest_size = strlen(source) + strlen(to_quoted) + 1;
|
||||
dest = xcalloc(dest_size, sizeof(char));
|
||||
|
||||
p1 = source;
|
||||
p2 = strstr(p1, from);
|
||||
if (p2 != NULL) {
|
||||
strncat(dest, p1, (size_t)(p2 - p1));
|
||||
strlcat(dest, to_quoted, dest_size);
|
||||
p1 = p2 + strlen(from);
|
||||
}
|
||||
strlcat(dest, p1, dest_size);
|
||||
|
||||
xfree(to_quoted);
|
||||
|
||||
return (dest);
|
||||
}
|
||||
|
||||
char *
|
||||
buildReencodeCommand(const char *extension, const char *fileName,
|
||||
metadata_t *mdata)
|
||||
metadata_t mdata)
|
||||
{
|
||||
cfg_decoder_t decoder;
|
||||
cfg_encoder_t encoder;
|
||||
@ -292,7 +225,7 @@ buildReencodeCommand(const char *extension, const char *fileName,
|
||||
if (strstr(dec_str, PLACEHOLDER_METADATA) != NULL) {
|
||||
if (cfg_get_metadata_program() &&
|
||||
cfg_get_metadata_format_str()) {
|
||||
char *mdataString = getMetadataString(cfg_get_metadata_format_str(),
|
||||
char *mdataString = stream_get_metadata_str(cfg_get_metadata_format_str(),
|
||||
mdata);
|
||||
char *tmpStr = replaceString(dec_str,
|
||||
PLACEHOLDER_METADATA, mdataString);
|
||||
@ -329,7 +262,7 @@ buildReencodeCommand(const char *extension, const char *fileName,
|
||||
if (strstr(enc_str, PLACEHOLDER_METADATA) != NULL) {
|
||||
if (cfg_get_metadata_program() &&
|
||||
cfg_get_metadata_format_str()) {
|
||||
char *mdataString = getMetadataString(cfg_get_metadata_format_str(),
|
||||
char *mdataString = stream_get_metadata_str(cfg_get_metadata_format_str(),
|
||||
mdata);
|
||||
char *tmpStr = replaceString(enc_str,
|
||||
PLACEHOLDER_METADATA, mdataString);
|
||||
@ -367,48 +300,10 @@ buildReencodeCommand(const char *extension, const char *fileName,
|
||||
return (commandString);
|
||||
}
|
||||
|
||||
char *
|
||||
getMetadataString(const char *format, metadata_t *mdata)
|
||||
{
|
||||
char *tmp, *str;
|
||||
|
||||
if (format == NULL)
|
||||
return (NULL);
|
||||
|
||||
str = xstrdup(format);
|
||||
|
||||
if (strstr(format, PLACEHOLDER_ARTIST) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_ARTIST,
|
||||
metadata_get_artist(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_TITLE) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_TITLE,
|
||||
metadata_get_title(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_STRING) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_STRING,
|
||||
metadata_get_string(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_TRACK) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_TRACK,
|
||||
metadata_get_filename(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
metadata_t *
|
||||
metadata_t
|
||||
getMetadata(const char *fileName)
|
||||
{
|
||||
metadata_t *mdata;
|
||||
metadata_t mdata;
|
||||
|
||||
if (cfg_get_metadata_program()) {
|
||||
if (NULL == (mdata = metadata_program(fileName,
|
||||
@ -433,92 +328,15 @@ getMetadata(const char *fileName)
|
||||
return (mdata);
|
||||
}
|
||||
|
||||
int
|
||||
setMetadata(shout_t *shout, metadata_t *mdata, char **mdata_copy)
|
||||
{
|
||||
shout_metadata_t *shout_mdata = NULL;
|
||||
char *songInfo;
|
||||
const char *artist, *title;
|
||||
int ret = SHOUTERR_SUCCESS;
|
||||
|
||||
if (cfg_get_metadata_no_updates())
|
||||
return (SHOUTERR_SUCCESS);
|
||||
|
||||
if (mdata == NULL)
|
||||
return 1;
|
||||
|
||||
if ((shout_mdata = shout_metadata_new()) == NULL) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_metadata_new");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
artist = metadata_get_artist(mdata);
|
||||
title = metadata_get_title(mdata);
|
||||
|
||||
/*
|
||||
* We can do this, because we know how libshout works. This adds
|
||||
* "charset=UTF-8" to the HTTP metadata update request and has the
|
||||
* desired effect of letting newer-than-2.3.1 versions of Icecast know
|
||||
* which encoding we're using.
|
||||
*/
|
||||
if (shout_metadata_add(shout_mdata, "charset", "UTF-8") != SHOUTERR_SUCCESS) {
|
||||
/* Assume SHOUTERR_MALLOC */
|
||||
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
songInfo = getMetadataString(cfg_get_metadata_format_str(), mdata);
|
||||
if (songInfo == NULL) {
|
||||
if (artist[0] == '\0' && title[0] == '\0')
|
||||
songInfo = xstrdup(metadata_get_string(mdata));
|
||||
else
|
||||
songInfo = metadata_assemble_string(mdata);
|
||||
if (artist[0] != '\0' && title[0] != '\0') {
|
||||
if (shout_metadata_add(shout_mdata, "artist", artist) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM,
|
||||
"shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
if (shout_metadata_add(shout_mdata, "title", title) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM,
|
||||
"shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (shout_metadata_add(shout_mdata, "song", songInfo) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM,
|
||||
"shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} else if (shout_metadata_add(shout_mdata, "song", songInfo) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((ret = shout_set_metadata(shout, shout_mdata)) != SHOUTERR_SUCCESS)
|
||||
log_warning("shout_set_metadata: %s", shout_get_error(shout));
|
||||
|
||||
shout_metadata_free(shout_mdata);
|
||||
|
||||
if (ret == SHOUTERR_SUCCESS) {
|
||||
if (mdata_copy != NULL && *mdata_copy == NULL)
|
||||
*mdata_copy = xstrdup(songInfo);
|
||||
}
|
||||
|
||||
xfree(songInfo);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
FILE *
|
||||
openResource(shout_t *shout, const char *fileName, int *popenFlag,
|
||||
metadata_t **mdata_p, int *isStdin, long *songLen)
|
||||
openResource(stream_t stream, const char *fileName, int *popenFlag,
|
||||
metadata_t *mdata_p, int *isStdin, long *songLen)
|
||||
{
|
||||
FILE *filep = NULL;
|
||||
char extension[25];
|
||||
char *p = NULL;
|
||||
char *pCommandString = NULL;
|
||||
metadata_t *mdata;
|
||||
metadata_t mdata;
|
||||
|
||||
if (mdata_p != NULL)
|
||||
*mdata_p = NULL;
|
||||
@ -529,7 +347,7 @@ openResource(shout_t *shout, const char *fileName, int *popenFlag,
|
||||
if (cfg_get_metadata_program()) {
|
||||
if ((mdata = getMetadata(cfg_get_metadata_program())) == NULL)
|
||||
return (NULL);
|
||||
if (setMetadata(shout, mdata, NULL) != SHOUTERR_SUCCESS) {
|
||||
if (stream_set_metadata(stream, mdata, NULL) != SHOUTERR_SUCCESS) {
|
||||
metadata_free(&mdata);
|
||||
return (NULL);
|
||||
}
|
||||
@ -699,15 +517,16 @@ getTimeString(long seconds)
|
||||
}
|
||||
|
||||
int
|
||||
sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
||||
sendStream(stream_t stream, FILE *filepstream, const char *fileName,
|
||||
int isStdin, const char *songLenStr, struct timespec *tv)
|
||||
{
|
||||
unsigned char buff[4096];
|
||||
size_t bytes_read, total, oldTotal;
|
||||
int ret;
|
||||
double kbps = -1.0;
|
||||
struct timespec timeStamp, *startTime = tv;
|
||||
struct timespec callTime, currentTime;
|
||||
unsigned char buff[4096];
|
||||
size_t bytes_read, total, oldTotal;
|
||||
int ret;
|
||||
double kbps = -1.0;
|
||||
struct timespec timeStamp, *startTime = tv;
|
||||
struct timespec callTime, currentTime;
|
||||
shout_t *shout = stream_get_shout(stream);
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &callTime);
|
||||
|
||||
@ -820,7 +639,7 @@ sendStream(shout_t *shout, FILE *filepstream, const char *fileName,
|
||||
}
|
||||
|
||||
int
|
||||
streamFile(shout_t *shout, const char *fileName)
|
||||
streamFile(stream_t stream, const char *fileName)
|
||||
{
|
||||
FILE *filepstream = NULL;
|
||||
int popenFlag = 0;
|
||||
@ -828,10 +647,10 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
int isStdin = 0;
|
||||
int ret, retval = 0;
|
||||
long songLen;
|
||||
metadata_t *mdata;
|
||||
metadata_t mdata;
|
||||
struct timespec startTime;
|
||||
|
||||
if ((filepstream = openResource(shout, fileName, &popenFlag, &mdata, &isStdin, &songLen))
|
||||
if ((filepstream = openResource(stream, fileName, &popenFlag, &mdata, &isStdin, &songLen))
|
||||
== NULL) {
|
||||
if (++resource_errors > 100) {
|
||||
log_error("too many errors; giving up");
|
||||
@ -854,7 +673,7 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
|
||||
/* MP3 streams are special, so set the metadata explicitly: */
|
||||
if (CFG_STREAM_MP3 == cfg_get_stream_format())
|
||||
setMetadata(shout, mdata, NULL);
|
||||
stream_set_metadata(stream, mdata, NULL);
|
||||
|
||||
metadata_free(&mdata);
|
||||
} else if (isStdin)
|
||||
@ -864,7 +683,7 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
songLenStr = xstrdup(getTimeString(songLen));
|
||||
clock_gettime(CLOCK_MONOTONIC, &startTime);
|
||||
do {
|
||||
ret = sendStream(shout, filepstream, fileName, isStdin,
|
||||
ret = sendStream(stream, filepstream, fileName, isStdin,
|
||||
songLenStr, &startTime);
|
||||
if (quit)
|
||||
break;
|
||||
@ -891,7 +710,7 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
continue;
|
||||
if (cfg_get_metadata_program()) {
|
||||
char *mdataStr = NULL;
|
||||
metadata_t *prog_mdata;
|
||||
metadata_t prog_mdata;
|
||||
|
||||
log_info("running metadata program: %s",
|
||||
cfg_get_metadata_program());
|
||||
@ -900,7 +719,7 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
ret = STREAM_DONE;
|
||||
continue;
|
||||
}
|
||||
if (setMetadata(shout, prog_mdata, &mdataStr) != SHOUTERR_SUCCESS) {
|
||||
if (stream_set_metadata(stream, prog_mdata, &mdataStr) != SHOUTERR_SUCCESS) {
|
||||
retval = 0;
|
||||
ret = STREAM_DONE;
|
||||
continue;
|
||||
@ -930,7 +749,7 @@ streamFile(shout_t *shout, const char *fileName)
|
||||
}
|
||||
|
||||
int
|
||||
streamPlaylist(shout_t *shout)
|
||||
streamPlaylist(stream_t stream)
|
||||
{
|
||||
const char *song;
|
||||
char lastSong[PATH_MAX];
|
||||
@ -968,7 +787,7 @@ streamPlaylist(shout_t *shout)
|
||||
|
||||
while ((song = playlist_get_next(playlist)) != NULL) {
|
||||
strlcpy(lastSong, song, sizeof(lastSong));
|
||||
if (!streamFile(shout, song))
|
||||
if (!streamFile(stream, song))
|
||||
return (0);
|
||||
if (quit)
|
||||
break;
|
||||
@ -1010,6 +829,7 @@ main(int argc, char *argv[])
|
||||
{
|
||||
int ret;
|
||||
const char *errstr;
|
||||
stream_t stream;
|
||||
shout_t *shout;
|
||||
extern char *optarg;
|
||||
extern int optind;
|
||||
@ -1034,8 +854,10 @@ main(int argc, char *argv[])
|
||||
return (ez_shutdown(2));
|
||||
}
|
||||
|
||||
if (NULL == (shout = stream_setup()))
|
||||
stream = stream_get(STREAM_DEFAULT);
|
||||
if (0 > stream_setup(stream))
|
||||
return (ez_shutdown(1));
|
||||
shout = stream_get_shout(stream);
|
||||
|
||||
#ifdef HAVE_SIGNALS
|
||||
memset(&act, 0, sizeof(act));
|
||||
@ -1078,9 +900,9 @@ main(int argc, char *argv[])
|
||||
|
||||
do {
|
||||
if (playlistMode) {
|
||||
cont = streamPlaylist(shout);
|
||||
cont = streamPlaylist(stream);
|
||||
} else {
|
||||
cont = streamFile(shout,
|
||||
cont = streamFile(stream,
|
||||
cfg_get_media_filename());
|
||||
}
|
||||
if (quit)
|
||||
|
@ -50,10 +50,6 @@
|
||||
#include "cfg.h"
|
||||
#include "log.h"
|
||||
|
||||
#ifndef STDIN_FILENO
|
||||
# define STDIN_FILENO 0
|
||||
#endif /* !STDIN_FILENO */
|
||||
|
||||
#ifndef _PATH_DEVNULL
|
||||
# define _PATH_DEVNULL "/dev/null"
|
||||
#endif /* !_PATH_DEVNULL */
|
||||
|
@ -73,21 +73,22 @@ struct ID3Tag {
|
||||
char genre;
|
||||
};
|
||||
|
||||
static metadata_t * metadata_create(const char *);
|
||||
static void metadata_use_taglib(metadata_t *, FILE **);
|
||||
static void metadata_use_self(metadata_t *, FILE **);
|
||||
static void metadata_clean_md(metadata_t *);
|
||||
static void metadata_get_extension(char *, size_t, const char *);
|
||||
static char * metadata_get_name(const char *);
|
||||
static void metadata_process_md(metadata_t *);
|
||||
static void metadata_normalize_string(char **);
|
||||
static struct metadata *
|
||||
metadata_create(const char *);
|
||||
static void metadata_use_taglib(struct metadata *, FILE **);
|
||||
static void metadata_use_self(struct metadata *, FILE **);
|
||||
static void metadata_clean_md(struct metadata *);
|
||||
static void metadata_get_extension(char *, size_t, const char *);
|
||||
static char * metadata_get_name(const char *);
|
||||
static void metadata_process_md(struct metadata *);
|
||||
static void metadata_normalize_string(char **);
|
||||
|
||||
static metadata_t *
|
||||
static struct metadata *
|
||||
metadata_create(const char *filename)
|
||||
{
|
||||
metadata_t *md;
|
||||
metadata_t md;
|
||||
|
||||
md = xcalloc(1UL, sizeof(metadata_t));
|
||||
md = xcalloc(1UL, sizeof(*md));
|
||||
md->filename = xstrdup(filename);
|
||||
md->songLen = -1;
|
||||
|
||||
@ -95,7 +96,7 @@ metadata_create(const char *filename)
|
||||
}
|
||||
|
||||
static void
|
||||
metadata_use_taglib(metadata_t *md, FILE **filep)
|
||||
metadata_use_taglib(struct metadata *md, FILE **filep)
|
||||
#ifdef HAVE_TAGLIB
|
||||
{
|
||||
TagLib_File *tf;
|
||||
@ -156,7 +157,7 @@ metadata_use_taglib(metadata_t *md, FILE **filep)
|
||||
#endif /* HAVE_TAGLIB */
|
||||
|
||||
static void
|
||||
metadata_use_self(metadata_t *md, FILE **filep)
|
||||
metadata_use_self(struct metadata *md, FILE **filep)
|
||||
#ifdef HAVE_TAGLIB
|
||||
{
|
||||
(void)md;
|
||||
@ -245,7 +246,7 @@ metadata_use_self(metadata_t *md, FILE **filep)
|
||||
#endif /* HAVE_TAGLIB */
|
||||
|
||||
static void
|
||||
metadata_clean_md(metadata_t *md)
|
||||
metadata_clean_md(struct metadata *md)
|
||||
{
|
||||
if (md->string != NULL) {
|
||||
xfree(md->string);
|
||||
@ -299,7 +300,7 @@ metadata_get_name(const char *file)
|
||||
}
|
||||
|
||||
static void
|
||||
metadata_process_md(metadata_t *md)
|
||||
metadata_process_md(struct metadata *md)
|
||||
{
|
||||
if (md->string == NULL)
|
||||
md->string = metadata_assemble_string(md);
|
||||
@ -342,10 +343,10 @@ metadata_normalize_string(char **s)
|
||||
*s = xreallocarray(tmpstr, strlen(tmpstr) + 1, sizeof(char));
|
||||
}
|
||||
|
||||
metadata_t *
|
||||
struct metadata *
|
||||
metadata_file(const char *filename, int normalize)
|
||||
{
|
||||
metadata_t *md;
|
||||
struct metadata *md;
|
||||
|
||||
md = metadata_create(filename);
|
||||
if (!metadata_file_update(md)) {
|
||||
@ -358,10 +359,10 @@ metadata_file(const char *filename, int normalize)
|
||||
return (md);
|
||||
}
|
||||
|
||||
metadata_t *
|
||||
struct metadata *
|
||||
metadata_program(const char *program, int normalize)
|
||||
{
|
||||
metadata_t *md;
|
||||
struct metadata *md;
|
||||
struct stat st;
|
||||
|
||||
if (stat(program, &st) == -1) {
|
||||
@ -386,9 +387,9 @@ metadata_program(const char *program, int normalize)
|
||||
}
|
||||
|
||||
void
|
||||
metadata_free(metadata_t **md_p)
|
||||
metadata_free(struct metadata **md_p)
|
||||
{
|
||||
metadata_t *md;
|
||||
struct metadata *md;
|
||||
|
||||
if (md_p == NULL || (md = *md_p) == NULL)
|
||||
return;
|
||||
@ -404,7 +405,7 @@ metadata_free(metadata_t **md_p)
|
||||
|
||||
|
||||
int
|
||||
metadata_file_update(metadata_t *md)
|
||||
metadata_file_update(struct metadata *md)
|
||||
{
|
||||
FILE *filep;
|
||||
|
||||
@ -427,7 +428,7 @@ metadata_file_update(metadata_t *md)
|
||||
}
|
||||
|
||||
int
|
||||
metadata_program_update(metadata_t *md, enum metadata_request md_req)
|
||||
metadata_program_update(struct metadata *md, enum metadata_request md_req)
|
||||
{
|
||||
FILE *filep;
|
||||
char buf[METADATA_MAX + 1];
|
||||
@ -534,14 +535,14 @@ metadata_program_update(metadata_t *md, enum metadata_request md_req)
|
||||
}
|
||||
|
||||
const char *
|
||||
metadata_get_string(metadata_t *md)
|
||||
metadata_get_string(struct metadata *md)
|
||||
{
|
||||
assert(md->string);
|
||||
return (md->string);
|
||||
}
|
||||
|
||||
const char *
|
||||
metadata_get_artist(metadata_t *md)
|
||||
metadata_get_artist(struct metadata *md)
|
||||
{
|
||||
if (md->artist == NULL)
|
||||
return (blankString);
|
||||
@ -550,7 +551,7 @@ metadata_get_artist(metadata_t *md)
|
||||
}
|
||||
|
||||
const char *
|
||||
metadata_get_title(metadata_t *md)
|
||||
metadata_get_title(struct metadata *md)
|
||||
{
|
||||
if (md->title == NULL)
|
||||
return (blankString);
|
||||
@ -559,7 +560,7 @@ metadata_get_title(metadata_t *md)
|
||||
}
|
||||
|
||||
const char *
|
||||
metadata_get_filename(metadata_t *md)
|
||||
metadata_get_filename(struct metadata *md)
|
||||
{
|
||||
if (md->filename == NULL)
|
||||
/* Should never happen: */
|
||||
@ -569,13 +570,13 @@ metadata_get_filename(metadata_t *md)
|
||||
}
|
||||
|
||||
int
|
||||
metadata_get_length(metadata_t *md)
|
||||
metadata_get_length(struct metadata *md)
|
||||
{
|
||||
return (md->songLen);
|
||||
}
|
||||
|
||||
char *
|
||||
metadata_assemble_string(metadata_t *md)
|
||||
metadata_assemble_string(struct metadata *md)
|
||||
{
|
||||
size_t len;
|
||||
char *str;
|
||||
|
@ -26,14 +26,14 @@ enum metadata_request {
|
||||
METADATA_TITLE
|
||||
};
|
||||
|
||||
typedef struct metadata metadata_t;
|
||||
typedef struct metadata * metadata_t;
|
||||
|
||||
/*
|
||||
* Read the metadata of a media file and return a new metadata handle on
|
||||
* success, or NULL on failure. The returned handle is "branded" for reading
|
||||
* metadata from media files.
|
||||
*/
|
||||
metadata_t * metadata_file(const char * /* filename */,
|
||||
metadata_t metadata_file(const char * /* filename */,
|
||||
int /* normalize strings */);
|
||||
|
||||
/*
|
||||
@ -53,26 +53,26 @@ metadata_t * metadata_file(const char * /* filename */,
|
||||
* metadata, or an empty string if no artist information is available.
|
||||
* - Return at most METADATA_MAX characters, or the result will be truncated.
|
||||
*/
|
||||
metadata_t * metadata_program(const char * /* program name */,
|
||||
metadata_t metadata_program(const char * /* program name */,
|
||||
int /* normalize strings */);
|
||||
|
||||
/*
|
||||
* Free all memory used by a metadata handle that has been created with
|
||||
* metadata_file() or metadata_program().
|
||||
*/
|
||||
void metadata_free(metadata_t **);
|
||||
void metadata_free(metadata_t *);
|
||||
|
||||
/*
|
||||
* Update/read the metadata for the given handle. Returns 1 on success, and 0
|
||||
* on failure.
|
||||
*/
|
||||
int metadata_file_update(metadata_t *);
|
||||
int metadata_file_update(metadata_t);
|
||||
|
||||
/*
|
||||
* Update/read the specified metadata for the given program-handle. Returns 1
|
||||
* on success, and 0 on failure.
|
||||
*/
|
||||
int metadata_program_update(metadata_t *, enum metadata_request);
|
||||
int metadata_program_update(metadata_t, enum metadata_request);
|
||||
|
||||
/*
|
||||
* Returns a pointer to a metadata string ``artist - title'', or just
|
||||
@ -81,34 +81,34 @@ int metadata_program_update(metadata_t *, enum metadata_request);
|
||||
* is returned for metadata_program() handles that didn't supply any generic
|
||||
* information.
|
||||
*/
|
||||
const char * metadata_get_string(metadata_t *);
|
||||
const char * metadata_get_string(metadata_t);
|
||||
|
||||
/*
|
||||
* Returns a pointer to the artist string, which may be empty.
|
||||
*/
|
||||
const char * metadata_get_artist(metadata_t *);
|
||||
const char * metadata_get_artist(metadata_t);
|
||||
|
||||
/*
|
||||
* Returns a pointer to the title string, which may be empty.
|
||||
*/
|
||||
const char * metadata_get_title(metadata_t *);
|
||||
const char * metadata_get_title(metadata_t);
|
||||
|
||||
/*
|
||||
* Returns a pointer to the filename used in the metadata handle.
|
||||
*/
|
||||
const char * metadata_get_filename(metadata_t *);
|
||||
const char * metadata_get_filename(metadata_t);
|
||||
|
||||
/*
|
||||
* Returns the length of the song, in seconds, or -1 if the information is not
|
||||
* available.
|
||||
*/
|
||||
int metadata_get_length(metadata_t *);
|
||||
int metadata_get_length(metadata_t);
|
||||
|
||||
/*
|
||||
* Allocates and returns a meaningful string based on a metadata handle's
|
||||
* content. The result is what metadata_get_string() defaults to if an external
|
||||
* program is not used.
|
||||
*/
|
||||
char * metadata_assemble_string(metadata_t *);
|
||||
char * metadata_assemble_string(metadata_t);
|
||||
|
||||
#endif /* __METADATA_H__ */
|
||||
|
414
src/stream.c
414
src/stream.c
@ -18,14 +18,252 @@
|
||||
# include "config.h"
|
||||
#endif /* HAVE_CONFIG_H */
|
||||
|
||||
#include <sys/queue.h>
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include <shout/shout.h>
|
||||
|
||||
#include "cfg.h"
|
||||
#include "log.h"
|
||||
#include "stream.h"
|
||||
#include "util.h"
|
||||
#include "xalloc.h"
|
||||
|
||||
struct stream {
|
||||
TAILQ_ENTRY(stream) entry;
|
||||
char *name;
|
||||
shout_t *shout;
|
||||
};
|
||||
TAILQ_HEAD(stream_list, stream);
|
||||
|
||||
static struct stream_list streams;
|
||||
|
||||
static int _stream_cfg_server(struct stream *);
|
||||
static int _stream_cfg_tls(struct stream *);
|
||||
static int _stream_cfg_stream(struct stream *);
|
||||
|
||||
static int
|
||||
_stream_cfg_server(struct stream *s)
|
||||
{
|
||||
switch (cfg_get_server_protocol()) {
|
||||
case CFG_PROTO_HTTP:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_protocol(s->shout, SHOUT_PROTOCOL_HTTP)) {
|
||||
log_error("%s: protocol: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("%s: protocol: unsupported: %s",
|
||||
s->name, cfg_get_server_protocol_str());
|
||||
return (-1);
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_host(s->shout, cfg_get_server_hostname())) {
|
||||
log_error("%s: hostname: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_port(s->shout, (unsigned short)cfg_get_server_port())) {
|
||||
log_error("%s: port: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_user(s->shout, cfg_get_server_user())) {
|
||||
log_error("%s: user: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_password(s->shout, cfg_get_server_password())) {
|
||||
log_error("%s: password: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_stream_cfg_tls(struct stream *s)
|
||||
{
|
||||
#ifdef SHOUT_TLS_AUTO
|
||||
int tls_req;
|
||||
|
||||
switch (cfg_get_server_tls()) {
|
||||
case CFG_TLS_NONE:
|
||||
tls_req = SHOUT_TLS_DISABLED;
|
||||
break;
|
||||
case CFG_TLS_MAY:
|
||||
tls_req = SHOUT_TLS_AUTO;
|
||||
break;
|
||||
case CFG_TLS_REQUIRED:
|
||||
tls_req = SHOUT_TLS_AUTO_NO_PLAIN;
|
||||
break;
|
||||
default:
|
||||
log_error("%s: tls: invalid", s->name);
|
||||
return (-1);
|
||||
}
|
||||
if (SHOUTERR_SUCCESS != shout_set_tls(s->shout, tls_req)) {
|
||||
log_error("%s: tls: %s", s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_server_ca_dir() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_ca_directory(s->shout, cfg_get_server_ca_dir())) {
|
||||
log_error("%s: ca_dir: %s", s->name,
|
||||
shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_server_ca_file() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_ca_file(s->shout, cfg_get_server_ca_file())) {
|
||||
log_error("%s: ca_file: %s", s->name,
|
||||
shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_server_tls_cipher_suite() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_allowed_ciphers(s->shout, cfg_get_server_tls_cipher_suite())) {
|
||||
log_error("%s: tls_cipher_suite: %s", s->name,
|
||||
shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_server_client_cert() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_client_certificate(s->shout, cfg_get_server_client_cert())) {
|
||||
log_error("%s: client_cert: %s", s->name,
|
||||
shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
#else /* SHOUT_TLS_AUTO */
|
||||
# warning "libshout library does not support TLS"
|
||||
switch (cfg_get_server_tls()) {
|
||||
case CFG_TLS_MAY:
|
||||
log_warning("%s: TLS optional but not supported by libshout",
|
||||
s->name);
|
||||
break;
|
||||
case CFG_TLS_REQUIRED:
|
||||
log_error("%s: TLS required by not supported by libshout",
|
||||
s->name);
|
||||
return (-1);
|
||||
default:
|
||||
break;
|
||||
}
|
||||
#endif /* SHOUT_TLS_AUTO */
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
static int
|
||||
_stream_cfg_stream(struct stream *s)
|
||||
{
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_mount(s->shout, cfg_get_stream_mountpoint())) {
|
||||
log_error("%s: mountpoint: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
switch (cfg_get_stream_format()) {
|
||||
case CFG_STREAM_VORBIS:
|
||||
case CFG_STREAM_THEORA:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_format(s->shout, SHOUT_FORMAT_OGG)) {
|
||||
log_error("%s: format_ogg: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
case CFG_STREAM_MP3:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_format(s->shout, SHOUT_FORMAT_MP3)) {
|
||||
log_error("%s: format_mp3: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("%s: format: unsupported: %s",
|
||||
s->name, cfg_get_stream_format_str());
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_name() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_name(s->shout, cfg_get_stream_name())) {
|
||||
log_error("%s: name: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_url() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_url(s->shout, cfg_get_stream_url())) {
|
||||
log_error("%s: url: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_genre() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_genre(s->shout, cfg_get_stream_genre())) {
|
||||
log_error("%s: genre: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_description() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_description(s->shout, cfg_get_stream_description())) {
|
||||
log_error("%s: description: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_quality() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(s->shout, SHOUT_AI_QUALITY, cfg_get_stream_quality())) {
|
||||
log_error("%s: ai_quality: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_bitrate() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(s->shout, SHOUT_AI_BITRATE, cfg_get_stream_bitrate())) {
|
||||
log_error("%s: ai_bitrate: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_samplerate() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(s->shout, SHOUT_AI_SAMPLERATE, cfg_get_stream_samplerate())) {
|
||||
log_error("%s: ai_samplerate: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (cfg_get_stream_channels() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(s->shout, SHOUT_AI_CHANNELS, cfg_get_stream_channels())) {
|
||||
log_error("%s: ai_channels: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_public(s->shout, (unsigned int)cfg_get_stream_server_public())) {
|
||||
log_error("%s: public: %s",
|
||||
s->name, shout_get_error(s->shout));
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
stream_init(void)
|
||||
{
|
||||
TAILQ_INIT(&streams);
|
||||
shout_init();
|
||||
return (0);
|
||||
}
|
||||
@ -33,5 +271,181 @@ stream_init(void)
|
||||
void
|
||||
stream_exit(void)
|
||||
{
|
||||
struct stream *e;
|
||||
|
||||
while (NULL != (e = TAILQ_FIRST(&streams))) {
|
||||
TAILQ_REMOVE(&streams, e, entry);
|
||||
xfree(e->name);
|
||||
shout_free(e->shout);
|
||||
xfree(e);
|
||||
}
|
||||
shout_shutdown();
|
||||
}
|
||||
|
||||
struct stream *
|
||||
stream_get(const char *name)
|
||||
{
|
||||
struct stream *e;
|
||||
|
||||
if (!name || !name[0]) {
|
||||
log_alert("stream_get: empty name");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
TAILQ_FOREACH(e, &streams, entry) {
|
||||
if (0 == strcasecmp(e->name, name))
|
||||
return (e);
|
||||
}
|
||||
|
||||
e = xcalloc(1UL, sizeof(*e));
|
||||
e->name = xstrdup(name);
|
||||
e->shout = shout_new();
|
||||
if (NULL == e->shout) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_new");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
TAILQ_INSERT_TAIL(&streams, e, entry);
|
||||
|
||||
return (e);
|
||||
}
|
||||
|
||||
int
|
||||
stream_setup(struct stream *s)
|
||||
{
|
||||
if (0 != _stream_cfg_server(s) ||
|
||||
0 != _stream_cfg_tls(s) ||
|
||||
0 != _stream_cfg_stream(s)) {
|
||||
/* Reset handle on error */
|
||||
shout_free(s->shout);
|
||||
s->shout = shout_new();
|
||||
if (NULL == s->shout) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_new");
|
||||
exit(1);
|
||||
}
|
||||
return (-1);
|
||||
}
|
||||
|
||||
return (0);
|
||||
}
|
||||
|
||||
int
|
||||
stream_set_metadata(struct stream *s, metadata_t md, char **md_str)
|
||||
{
|
||||
shout_metadata_t *shout_md = NULL;
|
||||
char *songInfo;
|
||||
const char *artist, *title;
|
||||
int ret = SHOUTERR_SUCCESS;
|
||||
|
||||
if (cfg_get_metadata_no_updates())
|
||||
return (SHOUTERR_SUCCESS);
|
||||
|
||||
if (md == NULL)
|
||||
return 1;
|
||||
|
||||
if ((shout_md = shout_metadata_new()) == NULL) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_metadata_new");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
artist = metadata_get_artist(md);
|
||||
title = metadata_get_title(md);
|
||||
|
||||
/*
|
||||
* We can do this, because we know how libshout works. This adds
|
||||
* "charset=UTF-8" to the HTTP metadata update request and has the
|
||||
* desired effect of letting newer-than-2.3.1 versions of Icecast know
|
||||
* which encoding we're using.
|
||||
*/
|
||||
if (shout_metadata_add(shout_md, "charset", "UTF-8") != SHOUTERR_SUCCESS) {
|
||||
/* Assume SHOUTERR_MALLOC */
|
||||
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
songInfo = stream_get_metadata_str(cfg_get_metadata_format_str(), md);
|
||||
if (songInfo == NULL) {
|
||||
if (artist[0] == '\0' && title[0] == '\0')
|
||||
songInfo = xstrdup(metadata_get_string(md));
|
||||
else
|
||||
songInfo = metadata_assemble_string(md);
|
||||
if (artist[0] != '\0' && title[0] != '\0') {
|
||||
if (shout_metadata_add(shout_md, "artist", artist) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM,
|
||||
"shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
if (shout_metadata_add(shout_md, "title", title) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM,
|
||||
"shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
} else {
|
||||
if (shout_metadata_add(shout_md, "song", songInfo) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM,
|
||||
"shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
} else if (shout_metadata_add(shout_md, "song", songInfo) != SHOUTERR_SUCCESS) {
|
||||
log_syserr(ALERT, ENOMEM, "shout_metadata_add");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if ((ret = shout_set_metadata(s->shout, shout_md)) != SHOUTERR_SUCCESS)
|
||||
log_warning("shout_set_metadata: %s", shout_get_error(s->shout));
|
||||
|
||||
shout_metadata_free(shout_md);
|
||||
|
||||
if (ret == SHOUTERR_SUCCESS) {
|
||||
if (md_str != NULL && *md_str == NULL)
|
||||
*md_str = xstrdup(songInfo);
|
||||
}
|
||||
|
||||
xfree(songInfo);
|
||||
return (ret);
|
||||
}
|
||||
|
||||
char *
|
||||
stream_get_metadata_str(const char *format, metadata_t mdata)
|
||||
{
|
||||
char *tmp, *str;
|
||||
|
||||
if (format == NULL)
|
||||
return (NULL);
|
||||
|
||||
str = xstrdup(format);
|
||||
|
||||
if (strstr(format, PLACEHOLDER_ARTIST) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_ARTIST,
|
||||
metadata_get_artist(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_TITLE) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_TITLE,
|
||||
metadata_get_title(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_STRING) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_STRING,
|
||||
metadata_get_string(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
if (strstr(format, PLACEHOLDER_TRACK) != NULL) {
|
||||
tmp = replaceString(str, PLACEHOLDER_TRACK,
|
||||
metadata_get_filename(mdata));
|
||||
xfree(str);
|
||||
str = tmp;
|
||||
}
|
||||
|
||||
return (str);
|
||||
}
|
||||
|
||||
shout_t *
|
||||
stream_get_shout(struct stream *s)
|
||||
{
|
||||
return (s->shout);
|
||||
}
|
||||
|
17
src/stream.h
17
src/stream.h
@ -17,7 +17,24 @@
|
||||
#ifndef __STREAM_H__
|
||||
#define __STREAM_H__
|
||||
|
||||
#include <shout/shout.h>
|
||||
|
||||
#include "metadata.h"
|
||||
|
||||
#define STREAM_DEFAULT "default"
|
||||
|
||||
typedef struct stream * stream_t;
|
||||
|
||||
int stream_init(void);
|
||||
void stream_exit(void);
|
||||
|
||||
stream_t
|
||||
stream_get(const char *);
|
||||
int stream_setup(stream_t);
|
||||
int stream_set_metadata(stream_t, metadata_t, char **);
|
||||
char * stream_get_metadata_str(const char *, metadata_t);
|
||||
|
||||
shout_t *
|
||||
stream_get_shout(stream_t);
|
||||
|
||||
#endif /* __STREAM_H__ */
|
||||
|
215
src/util.c
215
src/util.c
@ -22,14 +22,18 @@
|
||||
# include "config.h"
|
||||
#endif
|
||||
|
||||
#include "ezstream.h"
|
||||
#include "compat.h"
|
||||
|
||||
#include <ctype.h>
|
||||
#include <errno.h>
|
||||
#ifdef HAVE_LANGINFO_H
|
||||
# include <langinfo.h>
|
||||
#endif
|
||||
#ifdef HAVE_LOCALE_H
|
||||
# include <locale.h>
|
||||
#endif
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef HAVE_ICONV
|
||||
# include <iconv.h>
|
||||
@ -81,152 +85,6 @@ strrcasecmp(const char *s, const char *sub)
|
||||
return (ret);
|
||||
}
|
||||
|
||||
shout_t *
|
||||
stream_setup(void)
|
||||
{
|
||||
shout_t *shout;
|
||||
|
||||
if ((shout = shout_new()) == NULL) {
|
||||
log_syserr(ERROR, ENOMEM, "shout_new");
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
switch (cfg_get_server_protocol()) {
|
||||
case CFG_PROTO_HTTP:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_protocol(shout, SHOUT_PROTOCOL_HTTP)) {
|
||||
log_error("shout_set_protocol: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("unsupported protocol: %s",
|
||||
cfg_get_server_protocol_str());
|
||||
goto error;
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_host(shout, cfg_get_server_hostname())) {
|
||||
log_error("shout_set_host: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_port(shout, (unsigned short)cfg_get_server_port())) {
|
||||
log_error("shout_set_port: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_user(shout, cfg_get_server_user())) {
|
||||
log_error("shout_set_user: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_password(shout, cfg_get_server_password())) {
|
||||
log_error("shout_set_password: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_mount(shout, cfg_get_stream_mountpoint())) {
|
||||
log_error("shout_set_mount: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
switch (cfg_get_stream_format()) {
|
||||
case CFG_STREAM_VORBIS:
|
||||
case CFG_STREAM_THEORA:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_format(shout, SHOUT_FORMAT_OGG)) {
|
||||
log_error("shout_set_format: OGG: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
case CFG_STREAM_MP3:
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_format(shout, SHOUT_FORMAT_MP3)) {
|
||||
log_error("shout_set_format: MP3: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
log_error("unsupported stream format: %s",
|
||||
cfg_get_stream_format_str());
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (cfg_get_stream_name() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_name(shout, cfg_get_stream_name())) {
|
||||
log_error("shout_set_name: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_url() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_url(shout, cfg_get_stream_url())) {
|
||||
log_error("shout_set_url: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_genre() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_genre(shout, cfg_get_stream_genre())) {
|
||||
log_error("shout_set_genre: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_description() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_description(shout, cfg_get_stream_description())) {
|
||||
log_error("shout_set_description: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_quality() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_QUALITY,
|
||||
cfg_get_stream_quality())) {
|
||||
log_error("shout_set_audio_info: QUALITY: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_bitrate() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_BITRATE,
|
||||
cfg_get_stream_bitrate())) {
|
||||
log_error("shout_set_audio_info: BITRATE: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_samplerate() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_SAMPLERATE,
|
||||
cfg_get_stream_samplerate())) {
|
||||
log_error("shout_set_audio_info: SAMPLERATE: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
if (cfg_get_stream_channels() &&
|
||||
SHOUTERR_SUCCESS !=
|
||||
shout_set_audio_info(shout, SHOUT_AI_CHANNELS,
|
||||
cfg_get_stream_channels())) {
|
||||
log_error("shout_set_audio_info: CHANNELS: %s",
|
||||
shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
if (SHOUTERR_SUCCESS !=
|
||||
shout_set_public(shout, (unsigned int)cfg_get_stream_server_public())) {
|
||||
log_error("shout_set_public: %s", shout_get_error(shout));
|
||||
goto error;
|
||||
}
|
||||
|
||||
return (shout);
|
||||
|
||||
error:
|
||||
shout_free(shout);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
char *
|
||||
CHARtoUTF8(const char *in_str, int mode)
|
||||
{
|
||||
@ -358,3 +216,66 @@ iconvert(const char *in_str, const char *from, const char *to, int mode)
|
||||
return (xstrdup(in_str));
|
||||
#endif /* HAVE_ICONV */
|
||||
}
|
||||
|
||||
char *
|
||||
replaceString(const char *source, const char *from, const char *to)
|
||||
{
|
||||
char *to_quoted, *dest;
|
||||
size_t dest_size;
|
||||
const char *p1, *p2;
|
||||
|
||||
to_quoted = shellQuote(to);
|
||||
dest_size = strlen(source) + strlen(to_quoted) + 1;
|
||||
dest = xcalloc(dest_size, sizeof(char));
|
||||
|
||||
p1 = source;
|
||||
p2 = strstr(p1, from);
|
||||
if (p2 != NULL) {
|
||||
strncat(dest, p1, (size_t)(p2 - p1));
|
||||
strlcat(dest, to_quoted, dest_size);
|
||||
p1 = p2 + strlen(from);
|
||||
}
|
||||
strlcat(dest, p1, dest_size);
|
||||
|
||||
xfree(to_quoted);
|
||||
|
||||
return (dest);
|
||||
}
|
||||
|
||||
#define SHELLQUOTE_INLEN_MAX 8191UL
|
||||
|
||||
char *
|
||||
shellQuote(const char *in)
|
||||
{
|
||||
char *out, *out_p;
|
||||
size_t out_len;
|
||||
const char *in_p;
|
||||
|
||||
out_len = (strlen(in) > SHELLQUOTE_INLEN_MAX)
|
||||
? SHELLQUOTE_INLEN_MAX
|
||||
: strlen(in);
|
||||
out_len = out_len * 2 + 2;
|
||||
out = xcalloc(out_len + 1, sizeof(char));
|
||||
|
||||
out_p = out;
|
||||
in_p = in;
|
||||
|
||||
*out_p++ = '\'';
|
||||
out_len--;
|
||||
while (*in_p && out_len > 2) {
|
||||
switch (*in_p) {
|
||||
case '\'':
|
||||
case '\\':
|
||||
*out_p++ = '\\';
|
||||
out_len--;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
*out_p++ = *in_p++;
|
||||
out_len--;
|
||||
}
|
||||
*out_p++ = '\'';
|
||||
|
||||
return (out);
|
||||
}
|
||||
|
@ -16,16 +16,15 @@
|
||||
#ifndef __UTIL_H__
|
||||
#define __UTIL_H__
|
||||
|
||||
#include <shout/shout.h>
|
||||
|
||||
#define ICONV_REPLACE 0
|
||||
#define ICONV_TRANSLIT 1
|
||||
#define ICONV_IGNORE 2
|
||||
|
||||
int strrcmp(const char *, const char *);
|
||||
int strrcasecmp(const char *, const char *);
|
||||
shout_t * stream_setup(void);
|
||||
char * CHARtoUTF8(const char *, int);
|
||||
char * UTF8toCHAR(const char *, int);
|
||||
char * replaceString(const char *, const char *, const char *);
|
||||
char * shellQuote(const char *);
|
||||
|
||||
#endif /* __UTIL_H__ */
|
||||
|
Loading…
Reference in New Issue
Block a user