1
0
mirror of https://gitlab.xiph.org/xiph/ezstream.git synced 2025-02-02 15:07:45 -05:00

Make media inputs ("intakes") a list of things, too

This commit is contained in:
Moritz Grimm 2017-11-23 18:34:57 +01:00
parent 184e2dd8c3
commit 0b260b3c08
28 changed files with 872 additions and 335 deletions

View File

@ -307,6 +307,15 @@ The broadcast is private (the default).
.It Ar 1|Yes|True .It Ar 1|Yes|True
The broadcast is public. The broadcast is public.
.El .El
.It Sy \&<intake\ /\&>
Use the intake
.Po
input media
.Pc
configuration with the provided symbolic name for this stream.
.Pp
Default:
.Ar default
.It Sy \&<server\ /\&> .It Sy \&<server\ /\&>
Use the server configuration with the provided symbolic name for this stream. Use the server configuration with the provided symbolic name for this stream.
.Pp .Pp
@ -376,17 +385,41 @@ Informational number of audio channels of the broadcast.
Default: Default:
.Em none .Em none
.El .El
.Ss Media block .Ss Intakes block
.Bl -tag -width -Ds .Bl -tag -width -Ds
.It Sy \&<media\ /\&> .It Sy \&<intakes\ /\&>
.Pq Mandatory. This element contains all intake blocks as child elements.
This element contains the entire input media configuration as child elements.
Its parent is the Its parent is the
.Sy \&<ezstream\ /\&> .Sy \&<ezstream\ /\&>
element. element.
.Pp
A configuration file may contain multiple named intake configurations.
The stream configuration determines what intake
.Po
media input
.Pc
configuration should be used.
.El .El
.Ss Media configuration .Ss Intake block
.Bl -tag -width -Ds .Bl -tag -width -Ds
.It Sy \&<intake\ /\&>
.Pq Mandatory.
This element contains the entire input media configuration as child elements.
Its parent is the
.Sy \&<intakes\ /\&>
element.
.El
.Ss Intake configuration
.Bl -tag -width -Ds
.It Sy \&<name\ /\&>
Set the name of the intake configuration.
This may be referenced in a
.Sy \&<stream\ /\&> .
.Pp
The name is case-aware, but not case-sensitive.
.Pp
Default:
.Ar default
.It Sy \&<type\ /\&> .It Sy \&<type\ /\&>
Configure the input media type: Configure the input media type:
.Pp .Pp

View File

@ -26,9 +26,11 @@
</stream> </stream>
</streams> </streams>
<media> <intakes>
<intake>
<filename>%FILENAME%</filename> <filename>%FILENAME%</filename>
</media> </intake>
</intakes>
<decoders> <decoders>
<decoder> <decoder>

View File

@ -75,6 +75,9 @@
<!-- Mount point on server --> <!-- Mount point on server -->
<mountpoint>/stream.ogg</mountpoint> <mountpoint>/stream.ogg</mountpoint>
<!-- Name of the intake entry to use (default: "default") -->
<intake>Default</intake>
<!-- Name of the server entry to use (default: "default") --> <!-- Name of the server entry to use (default: "default") -->
<server>Default</server> <server>Default</server>
@ -100,9 +103,13 @@
</streams> </streams>
<!-- <!--
Media configuration Intake configuration
--> -->
<media> <intakes>
<intake>
<!-- Identifying name; default: "default" -->
<name>Default</name>
<!-- Media type: autodetect, file, playlist, program, stdin --> <!-- Media type: autodetect, file, playlist, program, stdin -->
<type>autodetect</type> <type>autodetect</type>
@ -112,9 +119,10 @@
<!-- Setting to shuffle playlists --> <!-- Setting to shuffle playlists -->
<shuffle>Yes</shuffle> <shuffle>Yes</shuffle>
<!-- Setting whether to stream input indefinitely or only once --> <!-- Setting whether to stream intake indefinitely or only once -->
<stream_once>No</stream_once> <stream_once>No</stream_once>
</media> </intake>
</intakes>
<!-- <!--
Metadata configuration Metadata configuration

View File

@ -22,8 +22,10 @@
</stream> </stream>
</streams> </streams>
<media> <intakes>
<intake>
<filename>playlist.m3u</filename> <filename>playlist.m3u</filename>
</media> </intake>
</intakes>
</ezstream> </ezstream>

View File

@ -22,8 +22,10 @@
</stream> </stream>
</streams> </streams>
<media> <intakes>
<intake>
<type>stdin</type> <type>stdin</type>
</media> </intake>
</intakes>
</ezstream> </ezstream>

View File

@ -21,9 +21,11 @@
</stream> </stream>
</streams> </streams>
<media> <intakes>
<intake>
<filename>playlist.m3u</filename> <filename>playlist.m3u</filename>
</media> </intake>
</intakes>
<decoders> <decoders>
<decoder> <decoder>

View File

@ -6,6 +6,7 @@ noinst_HEADERS = \
cfg.h \ cfg.h \
cfg_decoder.h \ cfg_decoder.h \
cfg_encoder.h \ cfg_encoder.h \
cfg_intake.h \
cfg_private.h \ cfg_private.h \
cfg_server.h \ cfg_server.h \
cfg_stream.h \ cfg_stream.h \
@ -22,6 +23,7 @@ libezstream_la_SOURCES = \
cfg.c \ cfg.c \
cfg_decoder.c \ cfg_decoder.c \
cfg_encoder.c \ cfg_encoder.c \
cfg_intake.c \
cfg_server.c \ cfg_server.c \
cfg_stream.c \ cfg_stream.c \
cfg_xmlfile.c \ cfg_xmlfile.c \

124
src/cfg.c
View File

@ -45,10 +45,11 @@ static void _cfg_commit(void);
static void static void
_cfg_reset(struct cfg *c) _cfg_reset(struct cfg *c)
{ {
cfg_server_list_destroy(&c->servers);
cfg_stream_list_destroy(&c->streams); cfg_stream_list_destroy(&c->streams);
cfg_decoder_list_destroy(&c->decoders); cfg_server_list_destroy(&c->servers);
cfg_intake_list_destroy(&c->intakes);
cfg_encoder_list_destroy(&c->encoders); cfg_encoder_list_destroy(&c->encoders);
cfg_decoder_list_destroy(&c->decoders);
xfree(c->metadata.format_str); xfree(c->metadata.format_str);
@ -133,47 +134,13 @@ cfg_file_reload(void)
} }
int int
cfg_check(const char **errstrp) cfg_check(const char **not_used)
{ {
if (!cfg_get_media_filename() && (void)not_used;
CFG_MEDIA_STDIN != cfg_get_media_type()) {
if (NULL != errstrp)
*errstrp = "media filename missing";
return (-1);
}
return (0); return (0);
} }
int
cfg_stream_str2fmt(const char *str, enum cfg_stream_format *fmt_p)
{
if (0 == strcasecmp(str, CFG_SFMT_VORBIS)) {
*fmt_p = CFG_STREAM_VORBIS;
} else if (0 == strcasecmp(str, CFG_SFMT_MP3)) {
*fmt_p = CFG_STREAM_MP3;
} else if (0 == strcasecmp(str, CFG_SFMT_THEORA)) {
*fmt_p = CFG_STREAM_THEORA;
} else
return (-1);
return (0);
}
const char *
cfg_stream_fmt2str(enum cfg_stream_format fmt)
{
switch (fmt) {
case CFG_STREAM_VORBIS:
return (CFG_SFMT_VORBIS);
case CFG_STREAM_MP3:
return (CFG_SFMT_MP3);
case CFG_STREAM_THEORA:
return (CFG_SFMT_THEORA);
default:
return (NULL);
}
}
int int
cfg_file_check(const char *file) cfg_file_check(const char *file)
{ {
@ -215,6 +182,15 @@ cfg_get_encoders(void)
return (cfg.encoders); return (cfg.encoders);
} }
cfg_intake_list_t
cfg_get_intakes(void)
{
if (!cfg.intakes)
cfg.intakes = cfg_intake_list_create();
return (cfg.intakes);
}
cfg_server_list_t cfg_server_list_t
cfg_get_servers(void) cfg_get_servers(void)
{ {
@ -290,54 +266,6 @@ cfg_set_program_verbosity(unsigned int verbosity, const char **not_used)
return (0); return (0);
} }
int
cfg_set_media_type(const char *type, const char **errstrp)
{
if (!type || !type[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 == strcasecmp("autodetect", type))
cfg.media.type = CFG_MEDIA_AUTODETECT;
else if (0 == strcasecmp("file", type))
cfg.media.type = CFG_MEDIA_FILE;
else if (0 == strcasecmp("playlist", type))
cfg.media.type = CFG_MEDIA_PLAYLIST;
else if (0 == strcasecmp("program", type))
cfg.media.type = CFG_MEDIA_PROGRAM;
else if (0 == strcasecmp("stdin", type))
cfg.media.type = CFG_MEDIA_STDIN;
else {
if (errstrp)
*errstrp = "unsupported";
return (-1);
}
return (0);
}
int
cfg_set_media_filename(const char *filename, const char **errstrp)
{
SET_STRLCPY(cfg.media.filename, filename, errstrp);
return (0);
}
int
cfg_set_media_shuffle(const char *shuffle, const char **errstrp)
{
SET_BOOLEAN(cfg.media.shuffle, shuffle, errstrp);
return (0);
}
int
cfg_set_media_stream_once(const char *stream_once, const char **errstrp)
{
SET_BOOLEAN(cfg.media.stream_once, stream_once, errstrp);
return (0);
}
int int
cfg_set_metadata_program(const char *program, const char **errstrp) cfg_set_metadata_program(const char *program, const char **errstrp)
{ {
@ -433,30 +361,6 @@ cfg_get_program_verbosity(void)
return (cfg_program.verbosity); return (cfg_program.verbosity);
} }
enum cfg_media_type
cfg_get_media_type(void)
{
return (cfg.media.type);
}
const char *
cfg_get_media_filename(void)
{
return (cfg.media.filename[0] ? cfg.media.filename : NULL);
}
int
cfg_get_media_shuffle(void)
{
return (cfg.media.shuffle);
}
int
cfg_get_media_stream_once(void)
{
return (cfg.media.stream_once);
}
const char * const char *
cfg_get_metadata_program(void) cfg_get_metadata_program(void)
{ {

View File

@ -17,10 +17,6 @@
#ifndef __CFG_H__ #ifndef __CFG_H__
#define __CFG_H__ #define __CFG_H__
#define CFG_SFMT_VORBIS "VORBIS"
#define CFG_SFMT_MP3 "MP3"
#define CFG_SFMT_THEORA "THEORA"
#define PLACEHOLDER_METADATA "@M@" #define PLACEHOLDER_METADATA "@M@"
#define PLACEHOLDER_ARTIST "@a@" #define PLACEHOLDER_ARTIST "@a@"
#define PLACEHOLDER_ALBUM "@b@" #define PLACEHOLDER_ALBUM "@b@"
@ -36,27 +32,9 @@ enum cfg_config_type {
CFG_TYPE_MAX = CFG_TYPE_XMLFILE, CFG_TYPE_MAX = CFG_TYPE_XMLFILE,
}; };
enum cfg_media_type {
CFG_MEDIA_AUTODETECT = 0,
CFG_MEDIA_FILE,
CFG_MEDIA_PLAYLIST,
CFG_MEDIA_PROGRAM,
CFG_MEDIA_STDIN,
CFG_MEDIA_MIN = CFG_MEDIA_AUTODETECT,
CFG_MEDIA_MAX = CFG_MEDIA_STDIN,
};
enum cfg_stream_format {
CFG_STREAM_INVALID = 0,
CFG_STREAM_VORBIS,
CFG_STREAM_MP3,
CFG_STREAM_THEORA,
CFG_STREAM_MIN = CFG_STREAM_VORBIS,
CFG_STREAM_MAX = CFG_STREAM_THEORA,
};
#include "cfg_decoder.h" #include "cfg_decoder.h"
#include "cfg_encoder.h" #include "cfg_encoder.h"
#include "cfg_intake.h"
#include "cfg_server.h" #include "cfg_server.h"
#include "cfg_stream.h" #include "cfg_stream.h"
@ -67,16 +45,14 @@ int cfg_file_reload(void);
int cfg_check(const char **); int cfg_check(const char **);
int cfg_stream_str2fmt(const char *, enum cfg_stream_format *);
const char *
cfg_stream_fmt2str(enum cfg_stream_format);
int cfg_file_check(const char *); int cfg_file_check(const char *);
cfg_decoder_list_t cfg_decoder_list_t
cfg_get_decoders(void); cfg_get_decoders(void);
cfg_encoder_list_t cfg_encoder_list_t
cfg_get_encoders(void); cfg_get_encoders(void);
cfg_intake_list_t
cfg_get_intakes(void);
cfg_server_list_t cfg_server_list_t
cfg_get_servers(void); cfg_get_servers(void);
cfg_stream_list_t cfg_stream_list_t
@ -90,11 +66,6 @@ int cfg_set_program_quiet_stderr(int, const char **);
int cfg_set_program_rtstatus_output(int, const char **); int cfg_set_program_rtstatus_output(int, const char **);
int cfg_set_program_verbosity(unsigned int, const char **); int cfg_set_program_verbosity(unsigned int, const char **);
int cfg_set_media_type(const char *, const char **);
int cfg_set_media_filename(const char *, const char **);
int cfg_set_media_shuffle(const char *, const char **);
int cfg_set_media_stream_once(const char *, const char **);
int cfg_set_metadata_program(const char *, const char **); int cfg_set_metadata_program(const char *, const char **);
int cfg_set_metadata_format_str(const char *, const char **); int cfg_set_metadata_format_str(const char *, const char **);
int cfg_set_metadata_refresh_interval(const char *, const char **); int cfg_set_metadata_refresh_interval(const char *, const char **);
@ -114,13 +85,6 @@ int cfg_get_program_rtstatus_output(void);
unsigned int unsigned int
cfg_get_program_verbosity(void); cfg_get_program_verbosity(void);
enum cfg_media_type
cfg_get_media_type(void);
const char *
cfg_get_media_filename(void);
int cfg_get_media_shuffle(void);
int cfg_get_media_stream_once(void);
const char * const char *
cfg_get_metadata_program(void); cfg_get_metadata_program(void);
const char * const char *

View File

@ -17,6 +17,8 @@
#ifndef __CFG_ENCODER_H__ #ifndef __CFG_ENCODER_H__
#define __CFG_ENCODER_H__ #define __CFG_ENCODER_H__
#include "cfg_stream.h"
typedef struct cfg_encoder * cfg_encoder_t; typedef struct cfg_encoder * cfg_encoder_t;
typedef struct cfg_encoder_list * cfg_encoder_list_t; typedef struct cfg_encoder_list * cfg_encoder_list_t;

247
src/cfg_intake.c Normal file
View File

@ -0,0 +1,247 @@
/*
* Copyright (c) 2017 Moritz Grimm <mgrimm@mrsserver.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif /* HAVE_CONFIG_H */
#include "compat.h"
#include <sys/queue.h>
#include <string.h>
#include "cfg_private.h"
#include "cfg_intake.h"
#include "xalloc.h"
struct cfg_intake {
TAILQ_ENTRY(cfg_intake) entry;
char *name;
enum cfg_intake_type type;
char filename[PATH_MAX];
int shuffle;
int stream_once;
};
TAILQ_HEAD(cfg_intake_list, cfg_intake);
struct cfg_intake_list *
cfg_intake_list_create(void)
{
struct cfg_intake_list *il;
il = xcalloc(1UL, sizeof(*il));
TAILQ_INIT(il);
return (il);
}
void
cfg_intake_list_destroy(cfg_intake_list_t *il_p)
{
struct cfg_intake_list *il = *il_p;
struct cfg_intake *i;
if (!il)
return;
while (NULL != (i = TAILQ_FIRST(il))) {
TAILQ_REMOVE(il, i, entry);
cfg_intake_destroy(&i);
}
xfree(il);
*il_p = NULL;
}
struct cfg_intake *
cfg_intake_list_find(struct cfg_intake_list *il, const char *name)
{
struct cfg_intake *i;
TAILQ_FOREACH(i, il, entry) {
if (0 == strcasecmp(i->name, name))
return (i);
}
return (NULL);
}
struct cfg_intake *
cfg_intake_list_get(struct cfg_intake_list *il, const char *name)
{
struct cfg_intake *i;
i = cfg_intake_list_find(il, name);
if (i)
return (i);
i = cfg_intake_create(name);
if (!i)
return (NULL);
TAILQ_INSERT_TAIL(il, i, entry);
return (i);
}
struct cfg_intake *
cfg_intake_create(const char *name)
{
struct cfg_intake *i;
if (!name || !name[0])
return (NULL);
i = xcalloc(1UL, sizeof(*i));
i->name = xstrdup(name);
return (i);
}
void
cfg_intake_destroy(struct cfg_intake **i_p)
{
struct cfg_intake *i = *i_p;
xfree(i->name);
xfree(i);
*i_p = NULL;
}
int
cfg_intake_set_name(struct cfg_intake *i, struct cfg_intake_list *il,
const char *name, const char **errstrp)
{
struct cfg_intake *i2;
if (!name || !name[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
i2 = cfg_intake_list_find(il, name);
if (i2 && i2 != i) {
if (errstrp)
*errstrp = "already exists";
return (-1);
}
SET_XSTRDUP(i->name, name, errstrp);
return (0);
}
int
cfg_intake_set_type(struct cfg_intake *i, struct cfg_intake_list *not_used,
const char *type, const char **errstrp)
{
(void)not_used;
if (!type || !type[0]) {
if (errstrp)
*errstrp = "empty";
return (-1);
}
if (0 == strcasecmp("autodetect", type))
i->type = CFG_INTAKE_AUTODETECT;
else if (0 == strcasecmp("file", type))
i->type = CFG_INTAKE_FILE;
else if (0 == strcasecmp("playlist", type))
i->type = CFG_INTAKE_PLAYLIST;
else if (0 == strcasecmp("program", type))
i->type = CFG_INTAKE_PROGRAM;
else if (0 == strcasecmp("stdin", type))
i->type = CFG_INTAKE_STDIN;
else {
if (errstrp)
*errstrp = "unsupported";
return (-1);
}
return (0);
}
int
cfg_intake_set_filename(struct cfg_intake *i, struct cfg_intake_list *not_used,
const char *filename, const char **errstrp)
{
(void)not_used;
SET_STRLCPY(i->filename, filename, errstrp);
return (0);
}
int
cfg_intake_set_shuffle(struct cfg_intake *i, struct cfg_intake_list *not_used,
const char *shuffle, const char **errstrp)
{
(void)not_used;
SET_BOOLEAN(i->shuffle, shuffle, errstrp);
return (0);
}
int
cfg_intake_set_stream_once(struct cfg_intake *i, struct cfg_intake_list *not_used,
const char *stream_once, const char **errstrp)
{
(void)not_used;
SET_BOOLEAN(i->stream_once, stream_once, errstrp);
return (0);
}
int
cfg_intake_validate(struct cfg_intake *i, const char **errstrp)
{
if (!cfg_intake_get_filename(i) &&
CFG_INTAKE_STDIN != cfg_intake_get_type(i)) {
if (NULL != errstrp)
*errstrp = "intake filename missing";
return (-1);
}
return (0);
}
const char *
cfg_intake_get_name(struct cfg_intake *i)
{
return (i->name);
}
enum cfg_intake_type
cfg_intake_get_type(struct cfg_intake *i)
{
return (i->type);
}
const char *
cfg_intake_get_filename(struct cfg_intake *i)
{
return (i->filename[0] ? i->filename : NULL);
}
int
cfg_intake_get_shuffle(struct cfg_intake *i)
{
return (i->shuffle);
}
int
cfg_intake_get_stream_once(struct cfg_intake *i)
{
return (i->stream_once);
}

68
src/cfg_intake.h Normal file
View File

@ -0,0 +1,68 @@
/*
* Copyright (c) 2017 Moritz Grimm <mgrimm@mrsserver.net>
*
* Permission to use, copy, modify, and distribute this software for any
* purpose with or without fee is hereby granted, provided that the above
* copyright notice and this permission notice appear in all copies.
*
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef __CFG_INTAKE_H__
#define __CFG_INTAKE_H__
enum cfg_intake_type {
CFG_INTAKE_AUTODETECT = 0,
CFG_INTAKE_FILE,
CFG_INTAKE_PLAYLIST,
CFG_INTAKE_PROGRAM,
CFG_INTAKE_STDIN,
CFG_INTAKE_MIN = CFG_INTAKE_AUTODETECT,
CFG_INTAKE_MAX = CFG_INTAKE_STDIN,
};
typedef struct cfg_intake * cfg_intake_t;
typedef struct cfg_intake_list * cfg_intake_list_t;
cfg_intake_list_t
cfg_intake_list_create(void);
void cfg_intake_list_destroy(cfg_intake_list_t *);
cfg_intake_t
cfg_intake_list_find(cfg_intake_list_t, const char *);
cfg_intake_t
cfg_intake_list_get(cfg_intake_list_t, const char *);
cfg_intake_t
cfg_intake_create(const char *);
void cfg_intake_destroy(cfg_intake_t *);
int cfg_intake_set_name(cfg_intake_t, cfg_intake_list_t, const char *,
const char **);
int cfg_intake_set_type(cfg_intake_t, cfg_intake_list_t, const char *,
const char **);
int cfg_intake_set_filename(cfg_intake_t, cfg_intake_list_t, const char *,
const char **);
int cfg_intake_set_shuffle(cfg_intake_t, cfg_intake_list_t, const char *,
const char **);
int cfg_intake_set_stream_once(cfg_intake_t, cfg_intake_list_t,
const char *, const char **);
int cfg_intake_validate(cfg_intake_t, const char **);
const char *
cfg_intake_get_name(cfg_intake_t);
enum cfg_intake_type
cfg_intake_get_type(cfg_intake_t);
const char *
cfg_intake_get_filename(cfg_intake_t);
int cfg_intake_get_shuffle(cfg_intake_t);
int cfg_intake_get_stream_once(cfg_intake_t);
#endif /* __CFG_INTAKE_H__ */

View File

@ -44,14 +44,11 @@ struct cfg_program {
}; };
struct cfg { struct cfg {
cfg_decoder_list_t decoders;
cfg_encoder_list_t encoders;
cfg_intake_list_t intakes;
cfg_server_list_t servers; cfg_server_list_t servers;
cfg_stream_list_t streams; cfg_stream_list_t streams;
struct media {
enum cfg_media_type type;
char filename[PATH_MAX];
int shuffle;
int stream_once;
} media;
struct metadata { struct metadata {
char program[PATH_MAX]; char program[PATH_MAX];
char *format_str; char *format_str;
@ -59,8 +56,6 @@ struct cfg {
int normalize_strings; int normalize_strings;
int no_updates; int no_updates;
} metadata; } metadata;
cfg_decoder_list_t decoders;
cfg_encoder_list_t encoders;
}; };
#define SET_STRLCPY(t, s, e) do { \ #define SET_STRLCPY(t, s, e) do { \

View File

@ -30,6 +30,7 @@ struct cfg_stream {
TAILQ_ENTRY(cfg_stream) entry; TAILQ_ENTRY(cfg_stream) entry;
char *name; char *name;
char *mountpoint; char *mountpoint;
char *intake;
char *server; char *server;
int public; int public;
enum cfg_stream_format format; enum cfg_stream_format format;
@ -126,6 +127,7 @@ cfg_stream_destroy(struct cfg_stream **s_p)
xfree(s->name); xfree(s->name);
xfree(s->mountpoint); xfree(s->mountpoint);
xfree(s->intake);
xfree(s->server); xfree(s->server);
xfree(s->encoder); xfree(s->encoder);
xfree(s->stream_name); xfree(s->stream_name);
@ -140,6 +142,36 @@ cfg_stream_destroy(struct cfg_stream **s_p)
*s_p = NULL; *s_p = NULL;
} }
int
cfg_stream_str2fmt(const char *str, enum cfg_stream_format *fmt_p)
{
if (0 == strcasecmp(str, CFG_SFMT_VORBIS)) {
*fmt_p = CFG_STREAM_VORBIS;
} else if (0 == strcasecmp(str, CFG_SFMT_MP3)) {
*fmt_p = CFG_STREAM_MP3;
} else if (0 == strcasecmp(str, CFG_SFMT_THEORA)) {
*fmt_p = CFG_STREAM_THEORA;
} else
return (-1);
return (0);
}
const char *
cfg_stream_fmt2str(enum cfg_stream_format fmt)
{
switch (fmt) {
case CFG_STREAM_VORBIS:
return (CFG_SFMT_VORBIS);
case CFG_STREAM_MP3:
return (CFG_SFMT_MP3);
case CFG_STREAM_THEORA:
return (CFG_SFMT_THEORA);
default:
return (NULL);
}
}
int int
cfg_stream_set_name(struct cfg_stream *s, struct cfg_stream_list *sl, cfg_stream_set_name(struct cfg_stream *s, struct cfg_stream_list *sl,
const char *name, const char **errstrp) const char *name, const char **errstrp)
@ -174,6 +206,15 @@ cfg_stream_set_mountpoint(struct cfg_stream *s,
return (0); return (0);
} }
int
cfg_stream_set_intake(struct cfg_stream *s, struct cfg_stream_list *not_used,
const char *intake, const char **errstrp)
{
(void)not_used;
SET_XSTRDUP(s->intake, intake, errstrp);
return (0);
}
int int
cfg_stream_set_server(struct cfg_stream *s, struct cfg_stream_list *not_used, cfg_stream_set_server(struct cfg_stream *s, struct cfg_stream_list *not_used,
const char *server, const char **errstrp) const char *server, const char **errstrp)
@ -330,6 +371,12 @@ cfg_stream_get_mountpoint(struct cfg_stream *s)
return (s->mountpoint); return (s->mountpoint);
} }
const char *
cfg_stream_get_intake(struct cfg_stream *s)
{
return (s->intake);
}
const char * const char *
cfg_stream_get_server(struct cfg_stream *s) cfg_stream_get_server(struct cfg_stream *s)
{ {

View File

@ -17,6 +17,19 @@
#ifndef __CFG_STREAM_H__ #ifndef __CFG_STREAM_H__
#define __CFG_STREAM_H__ #define __CFG_STREAM_H__
#define CFG_SFMT_VORBIS "VORBIS"
#define CFG_SFMT_MP3 "MP3"
#define CFG_SFMT_THEORA "THEORA"
enum cfg_stream_format {
CFG_STREAM_INVALID = 0,
CFG_STREAM_VORBIS,
CFG_STREAM_MP3,
CFG_STREAM_THEORA,
CFG_STREAM_MIN = CFG_STREAM_VORBIS,
CFG_STREAM_MAX = CFG_STREAM_THEORA,
};
typedef struct cfg_stream * cfg_stream_t; typedef struct cfg_stream * cfg_stream_t;
typedef struct cfg_stream_list * cfg_stream_list_t; typedef struct cfg_stream_list * cfg_stream_list_t;
@ -33,10 +46,16 @@ cfg_stream_t
cfg_stream_create(const char *); cfg_stream_create(const char *);
void cfg_stream_destroy(cfg_stream_t *); void cfg_stream_destroy(cfg_stream_t *);
int cfg_stream_str2fmt(const char *, enum cfg_stream_format *);
const char *
cfg_stream_fmt2str(enum cfg_stream_format);
int cfg_stream_set_name(cfg_stream_t, cfg_stream_list_t, const char *, int cfg_stream_set_name(cfg_stream_t, cfg_stream_list_t, const char *,
const char **); const char **);
int cfg_stream_set_mountpoint(cfg_stream_t, cfg_stream_list_t, int cfg_stream_set_mountpoint(cfg_stream_t, cfg_stream_list_t,
const char *, const char **); const char *, const char **);
int cfg_stream_set_intake(cfg_stream_t, cfg_stream_list_t, const char *,
const char **);
int cfg_stream_set_server(cfg_stream_t, cfg_stream_list_t, const char *, int cfg_stream_set_server(cfg_stream_t, cfg_stream_list_t, const char *,
const char **); const char **);
int cfg_stream_set_public(cfg_stream_t, cfg_stream_list_t, const char *, int cfg_stream_set_public(cfg_stream_t, cfg_stream_list_t, const char *,
@ -68,6 +87,8 @@ const char *
cfg_stream_get_name(cfg_stream_t); cfg_stream_get_name(cfg_stream_t);
const char * const char *
cfg_stream_get_mountpoint(cfg_stream_t); cfg_stream_get_mountpoint(cfg_stream_t);
const char *
cfg_stream_get_intake(cfg_stream_t);
const char * const char *
cfg_stream_get_server(cfg_stream_t); cfg_stream_get_server(cfg_stream_t);
int cfg_stream_get_public(cfg_stream_t); int cfg_stream_get_public(cfg_stream_t);

View File

@ -29,7 +29,8 @@ static int _cfg_xmlfile_parse_server(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_servers(xmlDocPtr, xmlNodePtr); static int _cfg_xmlfile_parse_servers(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_stream(xmlDocPtr, xmlNodePtr); static int _cfg_xmlfile_parse_stream(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_streams(xmlDocPtr, xmlNodePtr); static int _cfg_xmlfile_parse_streams(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_media(xmlDocPtr, xmlNodePtr); static int _cfg_xmlfile_parse_intake(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_intakes(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_metadata(xmlDocPtr, xmlNodePtr); static int _cfg_xmlfile_parse_metadata(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_decoder(xmlDocPtr, xmlNodePtr); static int _cfg_xmlfile_parse_decoder(xmlDocPtr, xmlNodePtr);
static int _cfg_xmlfile_parse_decoders(xmlDocPtr, xmlNodePtr); static int _cfg_xmlfile_parse_decoders(xmlDocPtr, xmlNodePtr);
@ -138,6 +139,7 @@ _cfg_xmlfile_parse_stream(xmlDocPtr doc, xmlNodePtr cur)
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) { for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_STREAM_SET(s, sl, cfg_stream_set_name, "name"); XML_STREAM_SET(s, sl, cfg_stream_set_name, "name");
XML_STREAM_SET(s, sl, cfg_stream_set_mountpoint, "mountpoint"); XML_STREAM_SET(s, sl, cfg_stream_set_mountpoint, "mountpoint");
XML_STREAM_SET(s, sl, cfg_stream_set_intake, "intake");
XML_STREAM_SET(s, sl, cfg_stream_set_server, "server"); XML_STREAM_SET(s, sl, cfg_stream_set_server, "server");
XML_STREAM_SET(s, sl, cfg_stream_set_public, "public"); XML_STREAM_SET(s, sl, cfg_stream_set_public, "public");
XML_STREAM_SET(s, sl, cfg_stream_set_format, "format"); XML_STREAM_SET(s, sl, cfg_stream_set_format, "format");
@ -172,6 +174,65 @@ _cfg_xmlfile_parse_streams(xmlDocPtr doc, xmlNodePtr cur)
return (0); return (0);
} }
#define XML_INPUT_SET(c, l, f, e) do { \
if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \
xmlChar *val; \
const char *err_str; \
int error = 0; \
\
val = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); \
if (0 > (f)((c), (l), STD_CHAR(val), &err_str)) { \
log_error("%s[%ld]: intake (%s): %s: %s", \
doc->name, xmlGetLineNo(cur), \
cfg_intake_get_name((c)), (e), err_str); \
error = 1; \
} \
xmlFree(val); \
if (error) \
return (-1); \
} \
} while (0)
static int
_cfg_xmlfile_parse_intake(xmlDocPtr doc, xmlNodePtr cur)
{
cfg_intake_list_t il;
cfg_intake_t i;
const char *errstr;
long int line_no = xmlGetLineNo(cur);
il = cfg_get_intakes();
i = cfg_intake_list_get(il, CFG_DEFAULT);
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_INPUT_SET(i, il, cfg_intake_set_name, "name");
XML_INPUT_SET(i, il, cfg_intake_set_type, "type");
XML_INPUT_SET(i, il, cfg_intake_set_filename, "filename");
XML_INPUT_SET(i, il, cfg_intake_set_shuffle, "shuffle");
XML_INPUT_SET(i, il, cfg_intake_set_stream_once, "stream_once");
}
if (0 > cfg_intake_validate(i, &errstr)) {
log_error("%s[%ld]: intake (%s): %s", doc->name, line_no,
cfg_intake_get_name(i), errstr);
return (-1);
}
return (0);
}
static int
_cfg_xmlfile_parse_intakes(xmlDocPtr doc, xmlNodePtr cur)
{
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("intake")) &&
0 > _cfg_xmlfile_parse_intake(doc, cur))
return (-1);
}
return (0);
}
#define XML_STRCONFIG(s, f, e) do { \ #define XML_STRCONFIG(s, f, e) do { \
if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \ if (0 == xmlStrcasecmp(cur->name, XML_CHAR((e)))) { \
xmlChar *val; \ xmlChar *val; \
@ -187,24 +248,6 @@ _cfg_xmlfile_parse_streams(xmlDocPtr doc, xmlNodePtr cur)
} \ } \
} while (0) } while (0)
static int
_cfg_xmlfile_parse_media(xmlDocPtr doc, xmlNodePtr cur)
{
int error = 0;
for (cur = cur->xmlChildrenNode; cur; cur = cur->next) {
XML_STRCONFIG("media", cfg_set_media_type, "type");
XML_STRCONFIG("media", cfg_set_media_filename, "filename");
XML_STRCONFIG("media", cfg_set_media_shuffle, "shuffle");
XML_STRCONFIG("media", cfg_set_media_stream_once, "stream_once");
}
if (error)
return (-1);
return (0);
}
static int static int
_cfg_xmlfile_parse_metadata(xmlDocPtr doc, xmlNodePtr cur) _cfg_xmlfile_parse_metadata(xmlDocPtr doc, xmlNodePtr cur)
{ {
@ -364,6 +407,7 @@ _cfg_xmlfile_parse_encoders(xmlDocPtr doc, xmlNodePtr cur)
* name * name
* mountpoint * mountpoint
* public * public
* intake
* server * server
* format * format
* encoder * encoder
@ -376,11 +420,13 @@ _cfg_xmlfile_parse_encoders(xmlDocPtr doc, xmlNodePtr cur)
* stream_samplerate * stream_samplerate
* stream_channels * stream_channels
* ... * ...
* media * intakes
* intake
* type * type
* filename * filename
* shuffle * shuffle
* stream_once * stream_once
* ...
* metadata * metadata
* program * program
* format_str * format_str
@ -441,8 +487,8 @@ cfg_xmlfile_parse(const char *config_file)
error = 1; error = 1;
continue; continue;
} }
if (0 == xmlStrcasecmp(cur->name, XML_CHAR("media"))) { if (0 == xmlStrcasecmp(cur->name, XML_CHAR("intakes"))) {
if (0 > _cfg_xmlfile_parse_media(doc, cur)) if (0 > _cfg_xmlfile_parse_intakes(doc, cur))
error = 1; error = 1;
continue; continue;
} }

View File

@ -407,6 +407,7 @@ sendStream(stream_t stream, FILE *filepstream, const char *fileName,
struct timespec timeStamp, *startTime = tv; struct timespec timeStamp, *startTime = tv;
struct timespec callTime, currentTime; struct timespec callTime, currentTime;
cfg_server_t cfg_server = stream_get_cfg_server(stream); cfg_server_t cfg_server = stream_get_cfg_server(stream);
cfg_intake_t cfg_intake = stream_get_cfg_intake(stream);
clock_gettime(CLOCK_MONOTONIC, &callTime); clock_gettime(CLOCK_MONOTONIC, &callTime);
@ -437,7 +438,7 @@ sendStream(stream_t stream, FILE *filepstream, const char *fileName,
break; break;
if (rereadPlaylist_notify) { if (rereadPlaylist_notify) {
rereadPlaylist_notify = 0; rereadPlaylist_notify = 0;
if (CFG_MEDIA_PLAYLIST == cfg_get_media_type()) if (CFG_INTAKE_PLAYLIST == cfg_intake_get_type(cfg_intake))
log_notice("HUP signal received: playlist re-read scheduled"); log_notice("HUP signal received: playlist re-read scheduled");
} }
if (skipTrack) { if (skipTrack) {
@ -464,8 +465,8 @@ sendStream(stream_t stream, FILE *filepstream, const char *fileName,
double oldTime, newTime; double oldTime, newTime;
if (!isStdin && playlistMode) { if (!isStdin && playlistMode) {
if (CFG_MEDIA_PROGRAM == cfg_get_media_type()) { if (CFG_INTAKE_PROGRAM == cfg_intake_get_type(cfg_intake)) {
char *tmp = xstrdup(cfg_get_media_filename()); char *tmp = xstrdup(cfg_intake_get_filename(cfg_intake));
printf(" [%s]", basename(tmp)); printf(" [%s]", basename(tmp));
xfree(tmp); xfree(tmp);
} else } else
@ -523,12 +524,13 @@ streamFile(stream_t stream, const char *fileName)
FILE *filepstream = NULL; FILE *filepstream = NULL;
int popenFlag = 0; int popenFlag = 0;
char *songLenStr = NULL; char *songLenStr = NULL;
int isStdin = cfg_get_media_type() == CFG_MEDIA_STDIN;
int ret, retval = 0; int ret, retval = 0;
long songLen; long songLen;
mdata_t md = NULL; mdata_t md = NULL;
struct timespec startTime; struct timespec startTime;
cfg_stream_t cfg_stream = stream_get_cfg_stream(stream); cfg_stream_t cfg_stream = stream_get_cfg_stream(stream);
cfg_intake_t cfg_intake = stream_get_cfg_intake(stream);
int isStdin = cfg_intake_get_type(cfg_intake) == CFG_INTAKE_STDIN;
if ((filepstream = openResource(stream, fileName, &popenFlag, &md, &isStdin, &songLen)) if ((filepstream = openResource(stream, fileName, &popenFlag, &md, &isStdin, &songLen))
== NULL) { == NULL) {
@ -633,23 +635,24 @@ streamPlaylist(stream_t stream)
{ {
const char *song; const char *song;
char lastSong[PATH_MAX]; char lastSong[PATH_MAX];
cfg_intake_t cfg_intake = stream_get_cfg_intake(stream);
if (playlist == NULL) { if (playlist == NULL) {
switch (cfg_get_media_type()) { switch (cfg_intake_get_type(cfg_intake)) {
case CFG_MEDIA_PROGRAM: case CFG_INTAKE_PROGRAM:
if ((playlist = playlist_program(cfg_get_media_filename())) == NULL) if ((playlist = playlist_program(cfg_intake_get_filename(cfg_intake))) == NULL)
return (0); return (0);
break; break;
case CFG_MEDIA_STDIN: case CFG_INTAKE_STDIN:
if ((playlist = playlist_read(NULL)) == NULL) if ((playlist = playlist_read(NULL)) == NULL)
return (0); return (0);
break; break;
default: default:
if ((playlist = playlist_read(cfg_get_media_filename())) == NULL) if ((playlist = playlist_read(cfg_intake_get_filename(cfg_intake))) == NULL)
return (0); return (0);
if (playlist_get_num_items(playlist) == 0) if (playlist_get_num_items(playlist) == 0)
log_warning("%s: playlist empty", log_warning("%s: playlist empty",
cfg_get_media_filename()); cfg_intake_get_filename(cfg_intake));
break; break;
} }
} else { } else {
@ -661,8 +664,8 @@ streamPlaylist(stream_t stream)
playlist_rewind(playlist); playlist_rewind(playlist);
} }
if (CFG_MEDIA_PROGRAM != cfg_get_media_type() && if (CFG_INTAKE_PROGRAM != cfg_intake_get_type(cfg_intake) &&
cfg_get_media_shuffle()) cfg_intake_get_shuffle(cfg_intake))
playlist_shuffle(playlist); playlist_shuffle(playlist);
while ((song = playlist_get_next(playlist)) != NULL) { while ((song = playlist_get_next(playlist)) != NULL) {
@ -673,12 +676,12 @@ streamPlaylist(stream_t stream)
break; break;
if (rereadPlaylist) { if (rereadPlaylist) {
rereadPlaylist = rereadPlaylist_notify = 0; rereadPlaylist = rereadPlaylist_notify = 0;
if (CFG_MEDIA_PROGRAM == cfg_get_media_type()) if (CFG_INTAKE_PROGRAM == cfg_intake_get_type(cfg_intake))
continue; continue;
log_notice("rereading playlist"); log_notice("rereading playlist");
if (!playlist_reread(&playlist)) if (!playlist_reread(&playlist))
return (0); return (0);
if (cfg_get_media_shuffle()) if (cfg_intake_get_shuffle(cfg_intake))
playlist_shuffle(playlist); playlist_shuffle(playlist);
else { else {
playlist_goto_entry(playlist, lastSong); playlist_goto_entry(playlist, lastSong);
@ -716,6 +719,7 @@ main(int argc, char *argv[])
unsigned int i; unsigned int i;
cfg_server_t cfg_server; cfg_server_t cfg_server;
cfg_stream_t cfg_stream; cfg_stream_t cfg_stream;
cfg_intake_t cfg_intake;
ret = 1; ret = 1;
if (0 > cfg_init() || if (0 > cfg_init() ||
@ -736,6 +740,7 @@ main(int argc, char *argv[])
return (ez_shutdown(1)); return (ez_shutdown(1));
cfg_server = stream_get_cfg_server(main_stream); cfg_server = stream_get_cfg_server(main_stream);
cfg_stream = stream_get_cfg_stream(main_stream); cfg_stream = stream_get_cfg_stream(main_stream);
cfg_intake = stream_get_cfg_intake(main_stream);
memset(&act, 0, sizeof(act)); memset(&act, 0, sizeof(act));
act.sa_handler = sig_handler; act.sa_handler = sig_handler;
@ -774,11 +779,11 @@ main(int argc, char *argv[])
cfg_server_get_port(cfg_server), cfg_server_get_port(cfg_server),
cfg_stream_get_mountpoint(cfg_stream)); cfg_stream_get_mountpoint(cfg_stream));
if (CFG_MEDIA_PROGRAM == cfg_get_media_type() || if (CFG_INTAKE_PROGRAM == cfg_intake_get_type(cfg_intake) ||
CFG_MEDIA_PLAYLIST == cfg_get_media_type() || CFG_INTAKE_PLAYLIST == cfg_intake_get_type(cfg_intake) ||
(CFG_MEDIA_AUTODETECT == cfg_get_media_type() && (CFG_INTAKE_AUTODETECT == cfg_intake_get_type(cfg_intake) &&
(util_strrcasecmp(cfg_get_media_filename(), ".m3u") == 0 || (util_strrcasecmp(cfg_intake_get_filename(cfg_intake), ".m3u") == 0 ||
util_strrcasecmp(cfg_get_media_filename(), ".txt") == 0))) util_strrcasecmp(cfg_intake_get_filename(cfg_intake), ".txt") == 0)))
playlistMode = 1; playlistMode = 1;
else else
playlistMode = 0; playlistMode = 0;
@ -788,11 +793,11 @@ main(int argc, char *argv[])
cont = streamPlaylist(main_stream); cont = streamPlaylist(main_stream);
} else { } else {
cont = streamFile(main_stream, cont = streamFile(main_stream,
cfg_get_media_filename()); cfg_intake_get_filename(cfg_intake));
} }
if (quit) if (quit)
break; break;
if (cfg_get_media_stream_once()) if (cfg_intake_get_stream_once(cfg_intake))
break; break;
} while (cont); } while (cont);

View File

@ -447,6 +447,19 @@ stream_get_cfg_stream(struct stream *s)
return (cfg_stream_list_find(cfg_get_streams(), s->name)); return (cfg_stream_list_find(cfg_get_streams(), s->name));
} }
cfg_intake_t
stream_get_cfg_intake(struct stream *s)
{
cfg_stream_t cfg_stream;
const char *intake;
cfg_stream = cfg_stream_list_get(cfg_get_streams(), s->name);
intake = cfg_stream_get_intake(cfg_stream);
if (!intake)
intake = CFG_DEFAULT;
return (cfg_intake_list_get(cfg_get_intakes(), intake));
}
cfg_server_t cfg_server_t
stream_get_cfg_server(struct stream *s) stream_get_cfg_server(struct stream *s)
{ {

View File

@ -39,6 +39,8 @@ const char *
int stream_get_connected(stream_t); int stream_get_connected(stream_t);
cfg_stream_t cfg_stream_t
stream_get_cfg_stream(stream_t); stream_get_cfg_stream(stream_t);
cfg_intake_t
stream_get_cfg_intake(stream_t);
cfg_server_t cfg_server_t
stream_get_cfg_server(stream_t); stream_get_cfg_server(stream_t);

View File

@ -4,6 +4,7 @@ TESTS = \
check_cfg \ check_cfg \
check_cfg_decoder \ check_cfg_decoder \
check_cfg_encoder \ check_cfg_encoder \
check_cfg_intake \
check_cfg_server \ check_cfg_server \
check_cfg_stream \ check_cfg_stream \
check_cfg_xmlfile \ check_cfg_xmlfile \
@ -30,6 +31,10 @@ check_cfg_encoder_SOURCES = check_cfg_encoder.c
check_cfg_encoder_DEPENDENCIES = $(top_builddir)/src/libezstream.la check_cfg_encoder_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_encoder_LDADD = $(check_cfg_encoder_DEPENDENCIES) @CHECK_LIBS@ check_cfg_encoder_LDADD = $(check_cfg_encoder_DEPENDENCIES) @CHECK_LIBS@
check_cfg_intake_SOURCES = check_cfg_intake.c
check_cfg_intake_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_intake_LDADD = $(check_cfg_intake_DEPENDENCIES) @CHECK_LIBS@
check_cfg_server_SOURCES = check_cfg_server.c check_cfg_server_SOURCES = check_cfg_server.c
check_cfg_server_DEPENDENCIES = $(top_builddir)/src/libezstream.la check_cfg_server_DEPENDENCIES = $(top_builddir)/src/libezstream.la
check_cfg_server_LDADD = $(check_cfg_server_DEPENDENCIES) @CHECK_LIBS@ check_cfg_server_LDADD = $(check_cfg_server_DEPENDENCIES) @CHECK_LIBS@
@ -87,6 +92,7 @@ EXTRA_DIST = \
config-bad3.xml \ config-bad3.xml \
config-bad4.xml \ config-bad4.xml \
config-bad5.xml \ config-bad5.xml \
config-bad6.xml \
play-bad.sh \ play-bad.sh \
play-bad2.sh \ play-bad2.sh \
play-bad3.sh \ play-bad3.sh \

View File

@ -13,49 +13,7 @@ void teardown_checked(void);
START_TEST(test_check) START_TEST(test_check)
{ {
const char *errstr = NULL;
ck_assert_int_eq(cfg_check(NULL), -1);
ck_assert_int_eq(cfg_check(&errstr), -1);
ck_assert_str_eq(errstr, "media filename missing");
ck_assert_int_eq(cfg_set_media_type("stdin", NULL), 0);
ck_assert_int_eq(cfg_check(NULL), 0); ck_assert_int_eq(cfg_check(NULL), 0);
ck_assert_int_eq(cfg_set_media_type("autodetect", NULL), 0);
ck_assert_int_eq(cfg_check(NULL), -1);
ck_assert_int_eq(cfg_check(&errstr), -1);
ck_assert_str_eq(errstr, "media filename missing");
ck_assert_int_eq(cfg_set_media_filename(SRCDIR "/playlist.txt", NULL),
0);
ck_assert_int_eq(cfg_check(NULL), 0);
}
END_TEST
START_TEST(test_stream_str2fmt)
{
enum cfg_stream_format fmt;
ck_assert_int_eq(cfg_stream_str2fmt(CFG_SFMT_VORBIS, &fmt), 0);
ck_assert_int_eq(fmt, CFG_STREAM_VORBIS);
ck_assert_int_eq(cfg_stream_str2fmt(CFG_SFMT_MP3, &fmt), 0);
ck_assert_int_eq(fmt, CFG_STREAM_MP3);
ck_assert_int_eq(cfg_stream_str2fmt(CFG_SFMT_THEORA, &fmt), 0);
ck_assert_int_eq(fmt, CFG_STREAM_THEORA);
ck_assert_int_eq(cfg_stream_str2fmt("<something else>", &fmt), -1);
}
END_TEST
START_TEST(test_stream_fmt2str)
{
ck_assert_str_eq(cfg_stream_fmt2str(CFG_STREAM_VORBIS),
CFG_SFMT_VORBIS);
ck_assert_str_eq(cfg_stream_fmt2str(CFG_STREAM_MP3),
CFG_SFMT_MP3);
ck_assert_str_eq(cfg_stream_fmt2str(CFG_STREAM_THEORA),
CFG_SFMT_THEORA);
ck_assert_ptr_eq(cfg_stream_fmt2str(CFG_STREAM_INVALID), NULL);
} }
END_TEST END_TEST
@ -121,46 +79,6 @@ START_TEST(test_program_verbosity)
} }
END_TEST END_TEST
START_TEST(test_media_type)
{
const char *errstr2;
TEST_EMPTYSTR(cfg_set_media_type);
ck_assert_int_eq(cfg_set_media_type("<something else>", &errstr2), -1);
ck_assert_str_eq(errstr2, "unsupported");
ck_assert_int_eq(cfg_set_media_type("aUtOdEtEcT", NULL), 0);
ck_assert_int_eq(cfg_get_media_type(), CFG_MEDIA_AUTODETECT);
ck_assert_int_eq(cfg_set_media_type("FiLe", NULL), 0);
ck_assert_int_eq(cfg_get_media_type(), CFG_MEDIA_FILE);
ck_assert_int_eq(cfg_set_media_type("pLaYlIsT", NULL), 0);
ck_assert_int_eq(cfg_get_media_type(), CFG_MEDIA_PLAYLIST);
ck_assert_int_eq(cfg_set_media_type("PrOgRaM", NULL), 0);
ck_assert_int_eq(cfg_get_media_type(), CFG_MEDIA_PROGRAM);
ck_assert_int_eq(cfg_set_media_type("sTdIn", NULL), 0);
ck_assert_int_eq(cfg_get_media_type(), CFG_MEDIA_STDIN);
}
END_TEST
START_TEST(test_media_filename)
{
TEST_STRLCPY(cfg_set_media_filename, cfg_get_media_filename, PATH_MAX);
}
END_TEST
START_TEST(test_media_shuffle)
{
TEST_BOOLEAN(cfg_set_media_shuffle, cfg_get_media_shuffle);
}
END_TEST
START_TEST(test_media_stream_once)
{
TEST_BOOLEAN(cfg_set_media_stream_once, cfg_get_media_stream_once);
}
END_TEST
START_TEST(test_metadata_program) START_TEST(test_metadata_program)
{ {
ck_assert_ptr_eq(cfg_get_metadata_program(), NULL); ck_assert_ptr_eq(cfg_get_metadata_program(), NULL);
@ -240,7 +158,6 @@ cfg_suite(void)
Suite *s; Suite *s;
TCase *tc_core; TCase *tc_core;
TCase *tc_program; TCase *tc_program;
TCase *tc_media;
TCase *tc_metadata; TCase *tc_metadata;
s = suite_create("Config"); s = suite_create("Config");
@ -248,8 +165,6 @@ cfg_suite(void)
tc_core = tcase_create("Core"); tc_core = tcase_create("Core");
tcase_add_checked_fixture(tc_core, setup_checked, teardown_checked); tcase_add_checked_fixture(tc_core, setup_checked, teardown_checked);
tcase_add_test(tc_core, test_check); tcase_add_test(tc_core, test_check);
tcase_add_test(tc_core, test_stream_str2fmt);
tcase_add_test(tc_core, test_stream_fmt2str);
tcase_add_test(tc_core, test_file_check); tcase_add_test(tc_core, test_file_check);
suite_add_tcase(s, tc_core); suite_add_tcase(s, tc_core);
@ -265,14 +180,6 @@ cfg_suite(void)
tcase_add_test(tc_program, test_program_verbosity); tcase_add_test(tc_program, test_program_verbosity);
suite_add_tcase(s, tc_program); suite_add_tcase(s, tc_program);
tc_media = tcase_create("Media");
tcase_add_checked_fixture(tc_media, setup_checked, teardown_checked);
tcase_add_test(tc_media, test_media_type);
tcase_add_test(tc_media, test_media_filename);
tcase_add_test(tc_media, test_media_shuffle);
tcase_add_test(tc_media, test_media_stream_once);
suite_add_tcase(s, tc_media);
tc_metadata = tcase_create("Metadata"); tc_metadata = tcase_create("Metadata");
tcase_add_checked_fixture(tc_metadata, setup_checked, tcase_add_checked_fixture(tc_metadata, setup_checked,
teardown_checked); teardown_checked);

170
tests/check_cfg_intake.c Normal file
View File

@ -0,0 +1,170 @@
#include <check.h>
#include <limits.h>
#include <netdb.h>
#include "cfg_private.h"
#include "log.h"
#include "check_cfg.h"
Suite * cfg_suite(void);
void setup_checked(void);
void teardown_checked(void);
cfg_intake_list_t intakes;
START_TEST(test_intake_list_get)
{
cfg_intake_t in, in2;
ck_assert_ptr_eq(cfg_intake_list_get(intakes, NULL), NULL);
ck_assert_ptr_eq(cfg_intake_list_get(intakes, ""), NULL);
in = cfg_intake_list_get(intakes, "TeSt");
in2 = cfg_intake_list_get(intakes, "test");
ck_assert_ptr_eq(in, in2);
}
END_TEST
START_TEST(test_intake_set_name)
{
TEST_XSTRDUP_T(cfg_intake_t, cfg_intake_list_get, intakes,
cfg_intake_set_name, cfg_intake_get_name);
}
END_TEST
START_TEST(test_intake_set_type)
{
const char *errstr2;
cfg_intake_t in = cfg_intake_list_get(intakes, "test_intake_set_type");
TEST_EMPTYSTR_T(cfg_intake_t, cfg_intake_list_get, intakes,
cfg_intake_set_type);
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "<something else>",
NULL), -1);
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "<something else>",
&errstr2), -1);
ck_assert_str_eq(errstr2, "unsupported");
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "aUtOdEtEcT", NULL),
0);
ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_AUTODETECT);
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "FiLe", NULL), 0);
ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_FILE);
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "pLaYlIsT", NULL),
0);
ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_PLAYLIST);
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "PrOgRaM", NULL), 0);
ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_PROGRAM);
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "sTdIn", NULL), 0);
ck_assert_int_eq(cfg_intake_get_type(in), CFG_INTAKE_STDIN);
}
END_TEST
START_TEST(test_intake_set_filename)
{
TEST_STRLCPY_T(cfg_intake_t, cfg_intake_list_get, intakes,
cfg_intake_set_filename, cfg_intake_get_filename, PATH_MAX);
}
END_TEST
START_TEST(test_intake_set_shuffle)
{
TEST_BOOLEAN_T(cfg_intake_t, cfg_intake_list_get, intakes,
cfg_intake_set_shuffle, cfg_intake_get_shuffle);
}
END_TEST
START_TEST(test_intake_set_stream_once)
{
TEST_BOOLEAN_T(cfg_intake_t, cfg_intake_list_get, intakes,
cfg_intake_set_stream_once, cfg_intake_get_stream_once);
}
END_TEST
START_TEST(test_intake_validate)
{
cfg_intake_t in = cfg_intake_list_get(intakes, "test_intake_validate");
const char *errstr;
ck_assert_int_ne(cfg_intake_validate(in, NULL), 0);
ck_assert_int_ne(cfg_intake_validate(in, &errstr), 0);
ck_assert_str_eq(errstr, "intake filename missing");
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "stdin", NULL), 0);
ck_assert_int_eq(cfg_intake_validate(in, NULL), 0);
ck_assert_int_eq(cfg_intake_set_type(in, intakes, "autodetect", NULL),
0);
ck_assert_int_ne(cfg_intake_validate(in, NULL), 0);
ck_assert_int_ne(cfg_intake_validate(in, &errstr), 0);
ck_assert_str_eq(errstr, "intake filename missing");
ck_assert_int_eq(cfg_intake_set_filename(in, intakes,
SRCDIR "/playlist.txt", NULL), 0);
ck_assert_int_eq(cfg_intake_validate(in, NULL), 0);
}
END_TEST
Suite *
cfg_suite(void)
{
Suite *s;
TCase *tc_intake;
s = suite_create("Config");
tc_intake = tcase_create("Intake");
tcase_add_checked_fixture(tc_intake, setup_checked,
teardown_checked);
tcase_add_test(tc_intake, test_intake_list_get);
tcase_add_test(tc_intake, test_intake_set_name);
tcase_add_test(tc_intake, test_intake_set_type);
tcase_add_test(tc_intake, test_intake_set_filename);
tcase_add_test(tc_intake, test_intake_set_shuffle);
tcase_add_test(tc_intake, test_intake_set_stream_once);
tcase_add_test(tc_intake, test_intake_validate);
suite_add_tcase(s, tc_intake);
return (s);
}
void
setup_checked(void)
{
if (0 < cfg_init() ||
0 < log_init())
ck_abort_msg("setup_checked failed");
intakes = cfg_intake_list_create();
}
void
teardown_checked(void)
{
cfg_intake_list_destroy(&intakes);
log_exit();
cfg_exit();
}
int
main(void)
{
int num_failed;
Suite *s;
SRunner *sr;
s = cfg_suite();
sr = srunner_create(s);
srunner_run_all(sr, CK_NORMAL);
num_failed = srunner_ntests_failed(sr);
srunner_free(sr);
if (num_failed)
return (1);
return (0);
}

View File

@ -26,6 +26,32 @@ START_TEST(test_stream_list_get)
} }
END_TEST END_TEST
START_TEST(test_stream_str2fmt)
{
enum cfg_stream_format fmt;
ck_assert_int_eq(cfg_stream_str2fmt(CFG_SFMT_VORBIS, &fmt), 0);
ck_assert_int_eq(fmt, CFG_STREAM_VORBIS);
ck_assert_int_eq(cfg_stream_str2fmt(CFG_SFMT_MP3, &fmt), 0);
ck_assert_int_eq(fmt, CFG_STREAM_MP3);
ck_assert_int_eq(cfg_stream_str2fmt(CFG_SFMT_THEORA, &fmt), 0);
ck_assert_int_eq(fmt, CFG_STREAM_THEORA);
ck_assert_int_eq(cfg_stream_str2fmt("<something else>", &fmt), -1);
}
END_TEST
START_TEST(test_stream_fmt2str)
{
ck_assert_str_eq(cfg_stream_fmt2str(CFG_STREAM_VORBIS),
CFG_SFMT_VORBIS);
ck_assert_str_eq(cfg_stream_fmt2str(CFG_STREAM_MP3),
CFG_SFMT_MP3);
ck_assert_str_eq(cfg_stream_fmt2str(CFG_STREAM_THEORA),
CFG_SFMT_THEORA);
ck_assert_ptr_eq(cfg_stream_fmt2str(CFG_STREAM_INVALID), NULL);
}
END_TEST
START_TEST(test_stream_name) START_TEST(test_stream_name)
{ {
} }
@ -38,6 +64,13 @@ START_TEST(test_stream_mountpoint)
} }
END_TEST END_TEST
START_TEST(test_stream_intake)
{
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
cfg_stream_set_intake, cfg_stream_get_intake);
}
END_TEST
START_TEST(test_stream_server) START_TEST(test_stream_server)
{ {
TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams, TEST_XSTRDUP_T(cfg_stream_t, cfg_stream_list_get, streams,
@ -164,8 +197,11 @@ cfg_suite(void)
tc_stream = tcase_create("Stream"); tc_stream = tcase_create("Stream");
tcase_add_checked_fixture(tc_stream, setup_checked, teardown_checked); tcase_add_checked_fixture(tc_stream, setup_checked, teardown_checked);
tcase_add_test(tc_stream, test_stream_list_get); tcase_add_test(tc_stream, test_stream_list_get);
tcase_add_test(tc_stream, test_stream_str2fmt);
tcase_add_test(tc_stream, test_stream_fmt2str);
tcase_add_test(tc_stream, test_stream_name); tcase_add_test(tc_stream, test_stream_name);
tcase_add_test(tc_stream, test_stream_mountpoint); tcase_add_test(tc_stream, test_stream_mountpoint);
tcase_add_test(tc_stream, test_stream_intake);
tcase_add_test(tc_stream, test_stream_server); tcase_add_test(tc_stream, test_stream_server);
tcase_add_test(tc_stream, test_stream_public); tcase_add_test(tc_stream, test_stream_public);
tcase_add_test(tc_stream, test_stream_format); tcase_add_test(tc_stream, test_stream_format);

View File

@ -40,6 +40,9 @@ START_TEST(test_reload)
ck_assert_int_eq(cfg_set_program_config_file(SRCDIR "/config-bad5.xml", ck_assert_int_eq(cfg_set_program_config_file(SRCDIR "/config-bad5.xml",
NULL), 0); NULL), 0);
ck_assert_int_eq(cfg_file_reload(), -1); ck_assert_int_eq(cfg_file_reload(), -1);
ck_assert_int_eq(cfg_set_program_config_file(SRCDIR "/config-bad6.xml",
NULL), 0);
ck_assert_int_eq(cfg_file_reload(), -1);
} }
END_TEST END_TEST

View File

@ -31,6 +31,7 @@ START_TEST(test_stream)
ck_assert_ptr_ne(srv_cfg, NULL); ck_assert_ptr_ne(srv_cfg, NULL);
str_cfg = stream_get_cfg_stream(s); str_cfg = stream_get_cfg_stream(s);
ck_assert_ptr_ne(str_cfg, NULL); ck_assert_ptr_ne(str_cfg, NULL);
ck_assert_ptr_ne(stream_get_cfg_intake(s), NULL);
ck_assert_int_ne(stream_configure(s), 0); ck_assert_int_ne(stream_configure(s), 0);
cfg_server_set_hostname(srv_cfg, servers, "localhost", NULL); cfg_server_set_hostname(srv_cfg, servers, "localhost", NULL);
@ -94,6 +95,8 @@ START_TEST(test_stream)
m_str = NULL; m_str = NULL;
mdata_destroy(&m); mdata_destroy(&m);
stream_destroy(&s);
} }
END_TEST END_TEST

View File

@ -25,6 +25,7 @@
<name></name> <name></name>
<mountpoint></mountpoint> <mountpoint></mountpoint>
<public></public> <public></public>
<intake></intake>
<server></server> <server></server>
<format></format> <format></format>
<encoder></encoder> <encoder></encoder>
@ -40,12 +41,15 @@
<!-- ... --> <!-- ... -->
</streams> </streams>
<media> <intakes>
<intake>
<name></name>
<type></type> <type></type>
<filename></filename> <filename></filename>
<shuffle></shuffle> <shuffle></shuffle>
<stream_once></stream_once> <stream_once></stream_once>
</media> </intake>
</intakes>
<metadata> <metadata>
<program></program> <program></program>

View File

@ -15,6 +15,17 @@
</server> </server>
</servers> </servers>
<intakes>
<intake>
<filename>secret</filename>
<name>duplicate</name>
</intake>
<intake>
<filename>secret</filename>
<name>duplicate</name>
</intake>
</intakes>
<streams> <streams>
<stream> <stream>
<format>vorbis</format> <format>vorbis</format>

32
tests/config-bad6.xml Normal file
View File

@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<ezstream>
<servers>
<server>
<hostname>127.0.0.1</hostname>
</server>
</servers>
<streams>
<stream>
<mountpoint>/stream.ogg</mountpoint>
</stream>
</streams>
<intakes>
<intake>
</intake>
</intakes>
<decoders>
<decoder>
</decoder>
</decoders>
<encoders>
<encoder>
</encoder>
</encoders>
</ezstream>