mirror of
https://gitlab.xiph.org/xiph/ezstream.git
synced 2024-12-04 14:46:31 -05:00
493 lines
9.5 KiB
C
493 lines
9.5 KiB
C
/*
|
|
* Copyright (c) 2017, 2020 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 <sys/queue.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include "cfg_private.h"
|
|
#include "cfg_stream.h"
|
|
#include "xalloc.h"
|
|
|
|
struct cfg_stream {
|
|
TAILQ_ENTRY(cfg_stream) entry;
|
|
char *name;
|
|
char *mountpoint;
|
|
char *intake;
|
|
char *server;
|
|
int public;
|
|
enum cfg_stream_format format;
|
|
char *encoder;
|
|
char *stream_name;
|
|
char *stream_url;
|
|
char *stream_genre;
|
|
char *stream_description;
|
|
char *stream_quality;
|
|
char *stream_bitrate;
|
|
char *stream_samplerate;
|
|
char *stream_channels;
|
|
};
|
|
|
|
TAILQ_HEAD(cfg_stream_list, cfg_stream);
|
|
|
|
struct cfg_stream_list *
|
|
cfg_stream_list_create(void)
|
|
{
|
|
struct cfg_stream_list *sl;
|
|
|
|
sl = xcalloc(1UL, sizeof(*sl));
|
|
TAILQ_INIT(sl);
|
|
|
|
return (sl);
|
|
}
|
|
|
|
void
|
|
cfg_stream_list_destroy(cfg_stream_list_t *sl_p)
|
|
{
|
|
struct cfg_stream_list *sl = *sl_p;
|
|
struct cfg_stream *s;
|
|
|
|
if (!sl)
|
|
return;
|
|
|
|
while (NULL != (s = TAILQ_FIRST(sl))) {
|
|
TAILQ_REMOVE(sl, s, entry);
|
|
cfg_stream_destroy(&s);
|
|
}
|
|
|
|
xfree(sl);
|
|
*sl_p = NULL;
|
|
}
|
|
|
|
unsigned int
|
|
cfg_stream_list_nentries(struct cfg_stream_list *sl)
|
|
{
|
|
struct cfg_stream *s;
|
|
unsigned int n = 0;
|
|
|
|
TAILQ_FOREACH(s, sl, entry) {
|
|
n++;
|
|
}
|
|
|
|
return (n);
|
|
}
|
|
|
|
struct cfg_stream *
|
|
cfg_stream_list_find(struct cfg_stream_list *sl, const char *name)
|
|
{
|
|
struct cfg_stream *s;
|
|
|
|
TAILQ_FOREACH(s, sl, entry) {
|
|
if (0 == strcasecmp(s->name, name))
|
|
return (s);
|
|
}
|
|
|
|
return (NULL);
|
|
}
|
|
|
|
struct cfg_stream *
|
|
cfg_stream_list_get(struct cfg_stream_list *sl, const char *name)
|
|
{
|
|
struct cfg_stream *s;
|
|
|
|
s = cfg_stream_list_find(sl, name);
|
|
if (s)
|
|
return (s);
|
|
s = cfg_stream_create(name);
|
|
if (!s)
|
|
return (NULL);
|
|
|
|
TAILQ_INSERT_TAIL(sl, s, entry);
|
|
|
|
return (s);
|
|
}
|
|
|
|
void
|
|
cfg_stream_list_foreach(struct cfg_stream_list *sl,
|
|
void (*cb)(cfg_stream_t, void *), void *cb_arg)
|
|
{
|
|
struct cfg_stream *s;
|
|
|
|
TAILQ_FOREACH(s, sl, entry) {
|
|
cb(s, cb_arg);
|
|
}
|
|
}
|
|
|
|
struct cfg_stream *
|
|
cfg_stream_create(const char *name)
|
|
{
|
|
struct cfg_stream *s;
|
|
|
|
if (!name || !name[0])
|
|
return (NULL);
|
|
|
|
s = xcalloc(1UL, sizeof(*s));
|
|
s->name = xstrdup(name);
|
|
|
|
return (s);
|
|
}
|
|
|
|
void
|
|
cfg_stream_destroy(struct cfg_stream **s_p)
|
|
{
|
|
struct cfg_stream *s = *s_p;
|
|
|
|
xfree(s->name);
|
|
xfree(s->mountpoint);
|
|
xfree(s->intake);
|
|
xfree(s->server);
|
|
xfree(s->encoder);
|
|
xfree(s->stream_name);
|
|
xfree(s->stream_url);
|
|
xfree(s->stream_genre);
|
|
xfree(s->stream_description);
|
|
xfree(s->stream_quality);
|
|
xfree(s->stream_bitrate);
|
|
xfree(s->stream_samplerate);
|
|
xfree(s->stream_channels);
|
|
xfree(s);
|
|
*s_p = NULL;
|
|
}
|
|
|
|
int
|
|
cfg_stream_str2fmt(const char *str, enum cfg_stream_format *fmt_p)
|
|
{
|
|
if (0 == strcasecmp(str, CFG_SFMT_OGG)) {
|
|
*fmt_p = CFG_STREAM_OGG;
|
|
} else if (0 == strcasecmp(str, CFG_SFMT_MP3)) {
|
|
*fmt_p = CFG_STREAM_MP3;
|
|
} else if (0 == strcasecmp(str, CFG_SFMT_WEBM)) {
|
|
*fmt_p = CFG_STREAM_WEBM;
|
|
} else if (0 == strcasecmp(str, CFG_SFMT_MATROSKA)) {
|
|
*fmt_p = CFG_STREAM_MATROSKA;
|
|
} else
|
|
return (-1);
|
|
return (0);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_fmt2str(enum cfg_stream_format fmt)
|
|
{
|
|
switch (fmt) {
|
|
case CFG_STREAM_OGG:
|
|
return (CFG_SFMT_OGG);
|
|
case CFG_STREAM_MP3:
|
|
return (CFG_SFMT_MP3);
|
|
case CFG_STREAM_WEBM:
|
|
return (CFG_SFMT_WEBM);
|
|
case CFG_STREAM_MATROSKA:
|
|
return (CFG_SFMT_MATROSKA);
|
|
default:
|
|
return (NULL);
|
|
}
|
|
}
|
|
|
|
|
|
int
|
|
cfg_stream_set_name(struct cfg_stream *s, struct cfg_stream_list *sl,
|
|
const char *name, const char **errstrp)
|
|
{
|
|
struct cfg_stream *s2;
|
|
|
|
if (!name || !name[0]) {
|
|
if (errstrp)
|
|
*errstrp = "empty";
|
|
return (-1);
|
|
}
|
|
|
|
s2 = cfg_stream_list_find(sl, name);
|
|
if (s2 && s2 != s) {
|
|
if (errstrp)
|
|
*errstrp = "already exists";
|
|
return (-1);
|
|
}
|
|
|
|
SET_XSTRDUP(s->name, name, errstrp);
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_mountpoint(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *mountpoint,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->mountpoint, mountpoint, errstrp);
|
|
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
|
|
cfg_stream_set_server(struct cfg_stream *s, struct cfg_stream_list *not_used,
|
|
const char *server, const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->server, server, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_public(struct cfg_stream *s, struct cfg_stream_list *not_used,
|
|
const char *public, const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_BOOLEAN(s->public, public, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_format(struct cfg_stream *s, struct cfg_stream_list *not_used,
|
|
const char *fmt_str, const char **errstrp)
|
|
{
|
|
enum cfg_stream_format fmt;
|
|
|
|
(void)not_used;
|
|
|
|
if (!fmt_str || !fmt_str[0]) {
|
|
if (errstrp)
|
|
*errstrp = "empty";
|
|
return (-1);
|
|
}
|
|
|
|
if (0 > cfg_stream_str2fmt(fmt_str, &fmt)) {
|
|
if (errstrp)
|
|
*errstrp = "unsupported stream format";
|
|
return (-1);
|
|
}
|
|
|
|
s->format = fmt;
|
|
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_encoder(struct cfg_stream *s, struct cfg_stream_list *not_used,
|
|
const char *encoder, const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
|
|
if (NULL == encoder) {
|
|
if (s->encoder)
|
|
xfree(s->encoder);
|
|
s->encoder = NULL;
|
|
return (0);
|
|
}
|
|
|
|
SET_XSTRDUP(s->encoder, encoder, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_name(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_name,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_name, stream_name, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_url(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_url,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_url, stream_url, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_genre(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_genre,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_genre, stream_genre, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_description(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_description,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_description, stream_description, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_quality(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_quality,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_quality, stream_quality, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_bitrate(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_bitrate,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_bitrate, stream_bitrate, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_samplerate(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_samplerate,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_samplerate, stream_samplerate, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_set_stream_channels(struct cfg_stream *s,
|
|
struct cfg_stream_list *not_used, const char *stream_channels,
|
|
const char **errstrp)
|
|
{
|
|
(void)not_used;
|
|
SET_XSTRDUP(s->stream_channels, stream_channels, errstrp);
|
|
return (0);
|
|
}
|
|
|
|
int
|
|
cfg_stream_validate(struct cfg_stream *s, const char **errstrp)
|
|
{
|
|
if (CFG_STREAM_INVALID == cfg_stream_get_format(s)) {
|
|
if (NULL != errstrp)
|
|
*errstrp = "format missing or unsupported";
|
|
return (-1);
|
|
}
|
|
|
|
return (0);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_name(struct cfg_stream *s)
|
|
{
|
|
return (s->name);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_mountpoint(struct cfg_stream *s)
|
|
{
|
|
return (s->mountpoint);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_intake(struct cfg_stream *s)
|
|
{
|
|
return (s->intake);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_server(struct cfg_stream *s)
|
|
{
|
|
return (s->server);
|
|
}
|
|
|
|
int
|
|
cfg_stream_get_public(struct cfg_stream *s)
|
|
{
|
|
return (s->public);
|
|
}
|
|
|
|
enum cfg_stream_format
|
|
cfg_stream_get_format(struct cfg_stream *s)
|
|
{
|
|
return (s->format);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_format_str(struct cfg_stream *s)
|
|
{
|
|
return (cfg_stream_fmt2str(s->format));
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_encoder(struct cfg_stream *s)
|
|
{
|
|
return (s->encoder);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_name(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_name);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_url(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_url);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_genre(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_genre);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_description(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_description);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_quality(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_quality);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_bitrate(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_bitrate);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_samplerate(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_samplerate);
|
|
}
|
|
|
|
const char *
|
|
cfg_stream_get_stream_channels(struct cfg_stream *s)
|
|
{
|
|
return (s->stream_channels);
|
|
}
|