Started on 2016-03-19, cooking for six weeks.
* refs/heads/t/kill-compiletime-ll:
Remove support for compile-time loglevel.
- New option --priority for para_server and para_audiod.
- New mood methods: image_id and lyrics_id.
- The manual and this NEWS file have been converted to markdown.
+- Support for the compile-time loglevel feature has been removed.
- Cleanup of the wma decoder and bitstream code.
- Improved wide-character support and fixes related to signal handling.
+- para_gui no longer reports 100% playing time at the stream start.
+- Opus cleanups.
Download: [tarball](./releases/paraslash-git.tar.bz2)
*/
char *get_time_string(void)
{
- int ret, seconds = 0, length;
+ int ret, seconds = 0, length = stat_task->length_seconds;
struct timeval *tmp, sum, sss, /* server stream start */
rstime, /* receiver start time */
wstime, /* writer start time */
if (audiod_status == AUDIOD_OFF)
goto empty;
- if (!(stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)) {
- if (stat_task->length_seconds) /* paused */
+ if (stat_task->server_stream_start.tv_sec == 0) {
+ if (stat_task->vss_status & VSS_STATUS_FLAG_PLAYING)
+ goto out; /* server is about to change file */
+ if (length > 0) /* paused */
return NULL;
goto empty; /* stopped */
}
- if (audiod_status == AUDIOD_ON && !s)
- goto empty;
/*
* Valid status items and playing, set length and tmp to the stream
* start. We use the slot info and fall back to the info from current
* status items if no slot info is available.
*/
- length = stat_task->length_seconds;
tmp = &stat_task->server_stream_start;
if (s && s->wns && s->wns[0].btrn) { /* writer active in this slot */
btr_get_node_start(s->wns[0].btrn, &wstime);
tv_diff(tmp, &stat_task->sa_time_diff, &sss);
else
tv_add(tmp, &stat_task->sa_time_diff, &sss);
- if (!s || !s->wns || !s->wns[0].btrn) {
+ if (!s || !s->wns || !s->wns[0].btrn || wstime.tv_sec == 0) {
struct timeval diff;
tv_diff(now, &sss, &diff);
seconds = diff.tv_sec + stat_task->offset_seconds;
if (s->receiver_node->btrn) {
btr_get_node_start(s->receiver_node->btrn, &rstime);
ret = tv_diff(&rstime, &sss, &rskip);
- if (ret > 0) { /* audiod was started in the middle of the stream */
+ if (ret > 0 && rskip.tv_sec > 2) {
+ /* audiod was started in the middle of the stream */
tv_add(&wtime, &rskip, &sum);
seconds += sum.tv_sec;
} else
static void close_unused_slots(void)
{
int i;
+ bool dump = false;
FOR_EACH_SLOT(i)
- if (must_close_slot(i))
+ if (must_close_slot(i)) {
close_slot(i);
+ dump = true;
+ }
+ if (dump)
+ audiod_status_dump(true);
}
/*
open_writers(sl);
activate_grab_clients(&sched);
btr_log_tree(sl->receiver_node->btrn, LL_NOTICE);
+ audiod_status_dump(true);
}
static void status_pre_select(struct sched *s, void *context)
AC_SUBST($1_ldflags)
])
+AC_USE_SYSTEM_EXTENSIONS
AC_C_BIGENDIAN()
AC_PATH_PROG([GENGETOPT], [gengetopt])
########################################################################### ucred
AC_MSG_CHECKING(for struct ucred)
AC_LINK_IFELSE([AC_LANG_PROGRAM([[
- #define _GNU_SOURCE
#include <sys/types.h>
#include <sys/socket.h>
]], [[
filters: $(echo $filters)
writers: $writers
-para_fade: $build_fade
para_server: $build_server
para_gui: $build_gui
para_fade: $build_fade
color_parse_or_die(p, me->log_colors[ll]);
return;
err:
- PARA_EMERG_LOG("%s: color syntax error\n", arg);
+ PARA_EMERG_LOG("%s: invalid color argument\n", arg);
exit(EXIT_FAILURE);
}
PARA_ERROR(HEADER_FREQ, "invalid header frequency"), \
PARA_ERROR(HEADER_BITRATE, "invalid header bitrate"), \
PARA_ERROR(ID3_DETACH, "could not detach id3 frame"), \
- PARA_ERROR(ID3_ATTACH, "could not atttach id3 frame"), \
+ PARA_ERROR(ID3_ATTACH, "could not attach id3 frame"), \
PARA_ERROR(ID3_SETENCODING, "could not set id3 text encoding field"), \
PARA_ERROR(ID3_SETSTRING, "could not set id3 string field"), \
DECLARE_FILTER_INITS
-/** The filter array, one structure for each supported filter. */
const struct filter *filter_get(int filter_num);
/** The array of supported filters. */
static struct filter filters[NUM_SUPPORTED_FILTERS] = {FILTER_ARRAY};
+/**
+ * Obtain a reference to a filter structure.
+ *
+ * \param filter_num Between zero and NUM_SUPPORTED_FILTERS, inclusively.
+ *
+ * \return Pointer to the filter identified by the given filter number.
+ *
+ * It is a fatal error if the given number is out of range. In this case
+ * the function aborts.
+ */
const struct filter *filter_get(int filter_num)
{
assert(filter_num >= 0);
#define __printf_2_3 __printf(2,3)
#define __printf_3_4 __printf(3,4)
-# if __GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ > 3)
-# define __must_check __attribute__ ((warn_unused_result))
-# else
-# define __must_check /* no warn_unused_result */
-# endif
-
+#define __must_check __attribute__ ((warn_unused_result))
#define _static_inline_ static inline
-
optional
default = "4"
details = "
- This value must be larger than the value given for above
- dccp-data-slices-per-group above. The difference being the
- number of redundant slices per group, i.e. the number of
- data packets that may be lost without causing interruptions
- of the resulting audio stream.
+ This value must be larger than the value of the argument to
+ --dccp-data-slices-per-group. The difference of the two values is
+ the number of redundant slices, that is, the number of slices which
+ may be lost without causing interruptions in the audio stream.
- Increase this value if for lossy networks.
+ Increase this value if you are on a lossy network.
"
####################
#~~~~~~~~~~~~~~~~
"select stream writer"
string typestr="name"
-default="alsa (file if alsa is unsupported)"
optional
multiple
details="
- May be give multiple times. The same writer may be specified
- more than once.
+ May be given multiple times, and the same writer may be specified more
+ than once. If this option is not given, the first supported writer
+ is started. The list of supported writers is shown in the help output.
"
include(channels.m4)
static void free_tag(struct id3_tag *id3_t)
{
int i, j;
+
+ if (!id3_t)
+ return;
for (i = 0; i < id3_t->nframes; i++) {
struct id3_frame *fr = id3_t->frames[i];
for (j = 0; j < fr->nfields; j++) {
if (v2_tag) {
PARA_NOTICE_LOG("replacing id3v2 tag\n");
old_v2size = v2_tag->paddedsize;
- } else if (!v1_tag) {
- PARA_NOTICE_LOG("no id3 tags found, adding id3v2 tag\n");
+ } else {
+ PARA_NOTICE_LOG("adding id3v2 tag\n");
v2_tag = id3_tag_new();
assert(v2_tag);
}
- if (v2_tag) {
- /*
- * Turn off all options to avoid creating an extended header.
- * id321 does not understand it.
- */
- id3_tag_options(v2_tag, ~0U, 0);
- ret = replace_tags(v2_tag, tags);
- if (ret < 0)
- goto out;
- new_v2size = id3_tag_render(v2_tag, NULL);
- v2_buffer = para_malloc(new_v2size);
- id3_tag_render(v2_tag, v2_buffer);
- PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size);
- ret = write_all(fd, (char *)v2_buffer, new_v2size);
- free(v2_buffer);
- if (ret < 0)
- goto out;
- }
+ /*
+ * Turn off all options to avoid creating an extended header. id321
+ * does not understand it.
+ */
+ id3_tag_options(v2_tag, ~0U, 0);
+ ret = replace_tags(v2_tag, tags);
+ if (ret < 0)
+ goto out;
+ new_v2size = id3_tag_render(v2_tag, NULL);
+ v2_buffer = para_malloc(new_v2size);
+ id3_tag_render(v2_tag, v2_buffer);
+ PARA_INFO_LOG("writing v2 tag (%lu bytes)\n", new_v2size);
+ ret = write_all(fd, (char *)v2_buffer, new_v2size);
+ free(v2_buffer);
+ if (ret < 0)
+ goto out;
data_sz = mapsize - old_v2size;
if (v1_tag && data_sz >= 128)
data_sz -= 128;
ret = write_all(fd, (char *)v1_buffer, 128);
}
out:
- if (v1_tag)
- free_tag(v1_tag);
- if (v2_tag)
- free_tag(v2_tag);
+ free_tag(v1_tag);
+ free_tag(v2_tag);
return ret;
}
/** \file net.c Networking-related helper functions. */
-/*
- * Since glibc 2.8, the _GNU_SOURCE feature test macro must be defined in order
- * to obtain the definition of the ucred structure.
- */
-#define _GNU_SOURCE
+#include "para.h"
#include <netinet/in.h>
#include <arpa/inet.h>
#include <regex.h>
-#include "para.h"
#include "error.h"
#include "net.h"
#include "string.h"
* handler.
*/
-#include <ogg/ogg.h>
-
#include "para.h"
#include "error.h"
#include "opus_common.h"
return 1;
}
-static int read_uint32(struct packet *p, ogg_uint32_t *val)
+static int read_uint32(struct packet *p, uint32_t *val)
{
if (p->pos > p->maxlen - 4)
return 0;
return 1;
}
-static int read_uint16(struct packet *p, ogg_uint16_t *val)
+static int read_uint16(struct packet *p, uint16_t *val)
{
if (p->pos > p->maxlen - 2)
return 0;
char str[9];
struct packet p;
unsigned char ch, channel_mapping;
- ogg_uint16_t shortval;
p.data = packet;
p.maxlen = len;
if (h->channels == 0)
return -E_OPUS_HEADER;
- if (!read_uint16(&p, &shortval))
+ if (!read_uint16(&p, &h->preskip))
return -E_OPUS_HEADER;
- h->preskip = shortval;
if (!read_uint32(&p, &h->input_sample_rate))
return -E_OPUS_HEADER;
- if (!read_uint16(&p, &shortval))
+ if (!read_uint16(&p, &h->gain))
return -E_OPUS_HEADER;
- h->gain = (short)shortval;
if (!read_chars(&p, &ch, 1))
return -E_OPUS_HEADER;
/** 1..255 */
int channels;
/** Number of bytes to skip from the beginning. */
- int preskip;
+ uint16_t preskip;
/** Sample rate of the input stream, used by the audio format handler. */
- ogg_uint32_t input_sample_rate;
+ uint32_t input_sample_rate;
/** In dB, should be zero whenever possible. */
- int gain;
+ uint16_t gain;
/** Number of logical streams (usually 1). */
int nb_streams;
/** Number of streams to decode as 2 channel streams. */
ogg_page ogg_page;
bool eos;
int channels;
- int preskip;
+ uint16_t preskip;
bool have_opus_stream;
bool have_more;
ogg_int32_t opus_serialno;
static void opusdec_add_output(short *pcm, int frames_available,
struct btr_node *btrn, struct opusdec_context *ctx)
{
- int tmp_skip, num_frames, bytes;
+ int num_frames, bytes;
+ uint16_t tmp_skip;
- tmp_skip = PARA_MIN(ctx->preskip, frames_available);
+ tmp_skip = PARA_MIN((int)ctx->preskip, frames_available);
ctx->preskip -= tmp_skip;
num_frames = frames_available - tmp_skip;
if (num_frames <= 0)
#include "para.h"
#include "list.h"
#include "play.cmdline.h"
-#include "filter.cmdline.h"
#include "error.h"
#include "ggo.h"
#include "buffer_tree.h"
get_internal_key_map_seq(key) : get_user_key_map_seq(key);
}
+static char *get_key_map_seq_safe(int key)
+{
+ const char hex[] = "0123456789abcdef";
+ char *seq = get_key_map_seq(key), *sseq;
+ size_t n, len = strlen(seq);
+
+ if (len == 1 && isprint(*seq))
+ return seq;
+ sseq = para_malloc(2 + 2 * len + 1);
+ sseq[0] = '0';
+ sseq[1] = 'x';
+ for (n = 0; n < len; n++) {
+ uint8_t val = (seq[n] & 0xf0) >> 4;
+ sseq[2 + 2 * n] = hex[val];
+ val = seq[n] & 0xf;
+ sseq[2 + 2 * n + 1] = hex[val];
+ }
+ free(seq);
+ sseq[2 + 2 * n] = '\0';
+ return sseq;
+}
+
static inline char *get_internal_key_map_cmd(int key)
{
return para_strdup(default_commands[get_internal_key_map_idx(key)]);
FOR_EACH_MAPPED_KEY(i) {
bool internal = is_internal_key(i);
int idx = get_key_map_idx(i);
- char *seq = get_key_map_seq(i);
+ char *seq = get_key_map_seq_safe(i);
char *cmd = get_key_map_cmd(i);
sz = xasprintf(&buf,
"%s key #%d: %s -> %s\n",
/** \file string.c Memory allocation and string handling functions. */
-#define _GNU_SOURCE
+#include "para.h"
#include <pwd.h>
#include <sys/utsname.h> /* uname() */
-
#include <string.h>
#include <regex.h>
-
#include <langinfo.h>
#include <wchar.h>
#include <wctype.h>
-#include "para.h"
#include "string.h"
#include "error.h"
/** \file user_list.h exported functions from user_list.c */
/**
- * permission flags that can be set individually for any server command
+ * Flags for server commands and user permissions.
*
- * - AFS_READ: read-only command of the audio file selector
- * - AFS_WRITE: command changes state of the audio file selector
- * - VSS_READ: command reads information about the current audio stream
- * - VSS_WRITE: command changes the current audio stream
+ * For each command, zero or more of these flags are ored to define the command
+ * permissions. A user is allowed to run the command if and only if all command
+ * permission flags are set for the user in the server.users config file which
+ * is read at server startup.
*/
-enum {AFS_READ = 1, AFS_WRITE = 2, VSS_READ = 4, VSS_WRITE = 8};
+enum server_command_permissions {
+ AFS_READ = 1, /** Read-only operation on the AFS database. */
+ AFS_WRITE = 2, /** Read-write operation on the AFS database. */
+ VSS_READ = 4, /** Read-only operation on the virtual streaming system. */
+ VSS_WRITE = 8 /** Read-write operation on the virtual streaming system. */
+};
/**
* data needed to authenticate the user
static char buf[512];
snprintf(buf, sizeof(buf) - 1, "%s\n"
- "Copyright (C) 2002-2015 Andre Noll\n"
+ "Copyright (C) 2002-2016 Andre Noll\n"
"This is free software with ABSOLUTELY NO WARRANTY."
" See COPYING for details.\n"
"Report bugs to <maan@tuebingen.mpg.de>.\n"
The image, lyrics, moods and playlists tables are all blob tables.
Blob tables consist of three columns each: The identifier which is
-a positive non-negative number that is auto-incremented, the name
-(an arbitrary string) and the content (the blob).
+a positive number that is auto-incremented, the name (an arbitrary
+string) and the content (the blob).
All blob tables support the same set of actions: cat, ls, mv, rm
and add. Of course, _add_ is used for adding new blobs to the table