This gets rid of the gengetopt grab client command line parser and all the special
treatment it caused.
m4_ggos := afh audioc audiod client filter gui recv server write
all_ggos := $(m4_ggos) dccp_recv oggdec_filter alsa_write oss_write fade http_recv \
osx_write udp_recv amp_filter compress_filter file_write \
- grab_client mp3dec_filter
+ mp3dec_filter
ggo_generated := $(addsuffix .cmdline.c, $(all_ggos)) $(addsuffix .cmdline.h, $(all_ggos)) \
$(addsuffix .ggo, $(addprefix $(ggo_dir)/,$(m4_ggos)))
#include "ggo.h"
#include "recv.h"
#include "filter.h"
-#include "grab_client.cmdline.h"
#include "grab_client.h"
#include "client.cmdline.h"
#include "client.h"
* \return The audio format number on success, -E_UNSUPPORTED_AUDIO_FORMAT if
* \a name is not a supported audio format.
*/
-int get_audio_format_num(char *name)
+int get_audio_format_num(const char *name)
{
int i;
stat_task->server_stream_start : *now;
s->offset_seconds = stat_task->offset_seconds;
s->seconds_total = stat_task->length_seconds;
- activate_inactive_grab_clients(slot_num, s->format, s->fc);
+ activate_inactive_grab_clients(s->format, s->fc);
}
static int open_receiver(int format)
N: grab
D: grab the audio stream
L:
-U: -- grab [grab_options]
+U: -- grab -[n=<num>] [-m[{s|p|a}]] [-i] [-o] [-f=<format>]
+H:
H: grab ('splice') the audio stream at any position in the filter
-H: chain and send that data back to the client. Try
-H: para_audioc -- grab -h
-H: for the list of available options.
+H: chain and send that data back to the client.
+H:
+H: Options:
+H:
+H: -n Point of the filter chain to grab. Filters count from zero.
+H:
+H: -m Change grab mode. Defaults to sloppy grab if not given.
+H:
+H: -ms: sloppy grab
+H:
+H: -mp: pedantic grab
+H:
+H: -ma: aggressive grab
+H:
+H: The various grab modes only differ in what happens if the
+H: file descriptor to write the grabbed audio data to is not
+H: ready for writing (i.e. would block). Sloppy mode ignores
+H: the write, pedantic mode aborts and aggressive mode tries
+H: to write anyway.
+H:
+H: -i Grab the filter input instead of its output.
+H:
+H: -o One-shot mode: Stop grabbing if audio file changes.
+H:
+H: -f Only grab streams of this format (mp3, ogg, aac). The default is to
+H: grab any stream.
+H:
---
N: help
D: display command list or help for given command
int num_filters(int audio_format_num);
-int get_audio_format_num(char *name);
+int get_audio_format_num(const char *name);
/** enum of audio formats supported by para_audiod */
enum {AUDIOD_AUDIO_FORMATS_ENUM};
#include "sched.h"
#include "ggo.h"
#include "filter.h"
-#include "grab_client.cmdline.h"
#include "grab_client.h"
#include "error.h"
return ret;
}
-#if 0
-static struct filter_node *find_filter_node(int slot_num, int format, int filternum)
+int com_grab(int fd, int argc, char **argv)
{
- int i;
-
- FOR_EACH_SLOT(i) {
- struct slot_info *s = &slot[i];
- if (s->format < 0 || !s->fc)
- continue;
- if (slot_num >= 0 && slot_num != i)
- continue;
- if (format >= 0 && s->format != format)
- continue;
- if (num_filters(i) <= filternum)
- continue;
- /* success */
- return s->fc->filter_nodes + filternum;
- }
- return NULL;
-}
-#endif
-
-int com_grab(int fd, __a_unused int argc, __a_unused char **argv)
-{
- client_write(fd, "grab is currently b0rken\n");
- close(fd);
- return 1;
-#if 0
- struct grab_client *gc;
- struct filter_node *fn;
- int i, err;
- char *msg;
-
- gc = grab_client_new(fd, cmdline, &err);
- if (!gc)
- goto err_out;
- fn = find_filter_node(gc->conf->slot_arg, gc->audio_format_num, gc->conf->filter_num_arg);
- if (fn)
- activate_grab_client(gc, fn);
- return 1;
-err_out:
- if (err != -E_GC_HELP_GIVEN && err != -E_GC_VERSION_GIVEN)
- return err;
- if (err == -E_GC_HELP_GIVEN) {
- msg = make_message("%s\n\n", grab_client_args_info_usage);
- for (i = 0; grab_client_args_info_help[i]; i++) {
- char *tmp = make_message("%s%s\n", msg,
- grab_client_args_info_help[i]);
- free(msg);
- msg = tmp;
- }
- } else
- msg = make_message("%s %s\n",
- GRAB_CLIENT_CMDLINE_PARSER_PACKAGE,
- GRAB_CLIENT_CMDLINE_PARSER_VERSION);
- err = client_write(fd, msg);
- free(msg);
- if (err < 0)
- return err;
- close(fd);
- return 1;
-#endif
+ return grab_client_new(fd, argc, argv);
}
__noreturn int com_term(int fd, __a_unused int argc, __a_unused char **argv)
}
return ret;
}
+
/**
* Send the current audiod status to all connected stat clients.
*/
audioc_errlist_objs="audioc string net fd"
audioc_ldflags=""
-audiod_cmdline_objs="audiod.cmdline grab_client.cmdline compress_filter.cmdline
+audiod_cmdline_objs="audiod.cmdline compress_filter.cmdline
http_recv.cmdline dccp_recv.cmdline file_write.cmdline client.cmdline
audiod_command_list amp_filter.cmdline udp_recv.cmdline
prebuffer_filter.cmdline sha1"
#define GRAB_CLIENT_ERRORS \
- PARA_ERROR(PEDANTIC_GRAB, "fd not ready and pedantic grab requested"), \
PARA_ERROR(GC_WRITE, "grab client write error"), \
PARA_ERROR(BAD_GC_SLOT, "invalid slot requested by grab client"), \
PARA_ERROR(BAD_GC_FILTER_NUM, "invalid filter number given"), \
PARA_ERROR(GC_SYNTAX, "grab client syntax error"), \
- PARA_ERROR(GC_HELP_GIVEN, ""), /* not really an error */ \
- PARA_ERROR(GC_VERSION_GIVEN, ""), /* not really an error */ \
#define MP3DEC_FILTER_ERRORS \
struct filter_callback {
/** All callbacks are organized in a doubly linked list. */
struct list_head node;
- /**
- * Private data.
- *
- * May be initialized by the application before registering the callback. This
- * pointer is not used by the filter subsystem. It is provided for use within
- * the input/output/close callback functions.
- */
- void *data;
/**
* The input callback.
*
- * In not \p NULL, the filter subsystem calls this function whenever the filter
+ * If not \p NULL, the filter subsystem calls this function whenever the filter
* consumed some or all of its input buffer. A pointer to the buffer of consumed
* data, its length and a pointer to the own \a filter_callback structure are passed
* to \a input_cb. The input callback is expected to return a negative value on errors.
*/
static void close_filter_callback(struct filter_callback *fcb)
{
- PARA_NOTICE_LOG("closing filter_callback %p, data: %p\n", fcb, fcb->data);
+ PARA_NOTICE_LOG("closing filter_callback %p\n", fcb);
list_del(&fcb->node);
fcb->close(fcb);
}
+++ /dev/null
-option "filter_num" f
- "point of filter chain to grab"
- int typestr="num"
- default="0"
- optional
-
-option "slot" s
- "only grab this slot; grab any slot if negative"
- int typestr="num"
- default="-1"
- optional
-
-option "audio_format" a
- "only grab this type of input stream;
- grab any if empty"
- string typestr="name"
- default=""
- optional
-
-option "input_grab" i
- "grab the filter input instead of its output"
- flag off
-
-option "one_shot" o
- "stop grabbing if audio file changes"
- flag off
-
-option "mode" m
- "select grab mode"
- typestr="grab_mode"
- values="sloppy","aggressive","pedantic"
- default="sloppy"
- optional
module_ggo_opts := --set-version="($(PACKAGE_STRING), $(codename))"
-grab_client.cmdline.h grab_client.cmdline.c: $(ggo_dir)/grab_client.ggo
- gengetopt $(module_ggo_opts) \
- -S \
- --set-package=grab \
- --no-handle-help \
- --no-handle-error \
- --no-handle-version \
- --arg-struct-name=grab_client_args_info \
- --file-name=$(subst .ggo,,$(<F)).cmdline \
- --func-name $(subst .ggo,,$(<F))_cmdline_parser < $<
-
%_recv.cmdline.h %_recv.cmdline.c: $(ggo_dir)/%_recv.ggo
gengetopt $(module_ggo_opts) \
--set-package=$(subst .ggo,,$(<F)) \
#include <dirent.h>
#include "para.h"
-#include "grab_client.cmdline.h"
#include "list.h"
#include "sched.h"
#include "ggo.h"
static int max_num_filters(void)
{
int i, ret = 0;
+
for (i = 0; audio_formats[i]; i++) {
PARA_INFO_LOG("%s filter chain length: %d\n", audio_formats[i],
num_filters(i));
return ret;
}
-static int gc_write(char *buf, size_t len, struct filter_callback *fcb)
+static struct filter_node *find_filter_node(int format, int filternum)
{
- struct grab_client *gc = fcb->data;
- struct timeval tv = {0, 100};
- int ret;
+ int i;
-// PARA_INFO_LOG("writing %d bytes to fd %d\n", len, gc->fd);
- fd_set wfds;
- FD_ZERO(&wfds);
- FD_SET(gc->fd, &wfds);
- ret = para_select(gc->fd + 1, NULL, &wfds, &tv);
- if (ret <= 0) {
- if (gc->mode == GRAB_PEDANTIC)
- return -E_PEDANTIC_GRAB;
- if (gc->mode == GRAB_SLOPPY)
- return 1;
- }
-rewrite:
- ret = write(gc->fd, buf, len);
- if (ret < 0) {
- ret = -E_GC_WRITE;
- gc->error = E_GC_WRITE;
- } else {
- if (ret != len) {
- if (gc->mode == GRAB_PEDANTIC)
- return -E_PEDANTIC_GRAB;
- if (gc->mode == GRAB_AGGRESSIVE) {
- len -= ret;
- memmove(buf, buf + ret, len);
- goto rewrite;
- }
- }
+ FOR_EACH_SLOT(i) {
+ struct slot_info *s = &slot[i];
+ if (s->format < 0 || !s->fc)
+ continue;
+ if (format >= 0 && s->format != format)
+ continue;
+ if (num_filters(i) <= filternum)
+ continue;
+ /* success */
+ return s->fc->filter_nodes + filternum;
}
- return ret;
+ return NULL;
}
-/* TODO: gengetopt can handle the grab client modes */
-static int check_gc_args(struct grab_client *gc)
+static int gc_write(char *buf, size_t len, struct filter_callback *fcb)
{
- int i;
- struct grab_client_args_info *c = gc->conf;
- const char **mv = grab_client_cmdline_parser_mode_values;
+ struct grab_client *gc = container_of(fcb, struct grab_client, fcb);
+ size_t written = 0;
- PARA_INFO_LOG("filter_num: %d\n", c->filter_num_arg);
- for (i = 0; mv[i]; i++)
- if (!strcmp(c->mode_arg, mv[i]))
- break;
- if (!mv[i])
- return -E_GC_SYNTAX;
- gc->mode = i;
- gc->audio_format_num = -1;
- if (c->audio_format_given) {
- gc->audio_format_num = get_audio_format_num(c->audio_format_arg);
- if (gc->audio_format_num < 0)
- return gc->audio_format_num;
+ while (written < len) {
+ int ret = write_ok(gc->fd);
+ if (ret < 0)
+ goto err;
+ if (ret == 0) { /* fd not ready */
+ if (gc->mode == GM_PEDANTIC)
+ goto err;
+ if (gc->mode == GM_SLOPPY)
+ return 1;
+ }
+ ret = write(gc->fd, buf + written, len - written);
+ if (ret < 0) {
+ if (errno != EAGAIN && errno != EINTR)
+ goto err;
+ if (gc->mode == GM_PEDANTIC)
+ goto err;
+ if (gc->mode == GM_SLOPPY)
+ return 1;
+ } else
+ written += ret;
}
- if (c->slot_arg >= MAX_STREAM_SLOTS)
- return -E_BAD_GC_SLOT;
- if (c->filter_num_arg < 0)
- return -E_BAD_GC_FILTER_NUM;
- if (c->audio_format_given) {
- if (num_filters(gc->audio_format_num) <= c->filter_num_arg)
- return -E_BAD_GC_FILTER_NUM;
- } else
- if (c->filter_num_arg >= max_num_filters())
- return -E_BAD_GC_FILTER_NUM;
-
return 1;
+err:
+ gc->error = -E_GC_WRITE;
+ return -E_GC_WRITE;
}
static void add_inactive_gc(struct grab_client *gc)
para_list_add(&gc->node, &inactive_grab_client_list);
}
-static void gc_free(struct grab_client *gc)
-{
- int i;
-
- for (i = 0; i < gc->argc; i++)
- free(gc->argv[i]);
- free(gc->argv);
- free(gc->conf);
- free(gc);
-
-}
-
static void gc_close(struct filter_callback *fcb)
{
- struct grab_client *gc = fcb->data;
+ struct grab_client *gc = container_of(fcb, struct grab_client, fcb);
- if (gc->conf->one_shot_given || gc->error) {
+ if ((gc->flags & GF_ONE_SHOT) || gc->error < 0) {
PARA_INFO_LOG("closing fd %d (grab client %p)\n", gc->fd, gc);
close(gc->fd);
- gc_free(gc);
- /* close on fork ?*/
+ free(gc);
return;
}
add_inactive_gc(gc);
/**
* Activate inactive grab clients if possible.
*
- * \param slot_num Audiod's slot for the new audio file.
* \param audio_format_num The number of the audio format of the new audio file.
* \param fc The filter chain containing the activated filters.
*
* \sa filter_chain_info::filters, inactive_grab_client_list,
* activate_grab_client.
*/
-void activate_inactive_grab_clients(int slot_num, int audio_format_num,
+void activate_inactive_grab_clients(int audio_format_num,
struct filter_chain *fc)
{
struct grab_client *gc, *tmp;
- int filter_num;
struct filter_node *fn;
list_for_each_entry_safe(gc, tmp, &inactive_grab_client_list, node) {
-// PARA_INFO_LOG("checking inactive grab client %p\n", gc);
- if (gc->conf->slot_arg >= 0 && gc->conf->slot_arg != slot_num)
- continue;
if (gc->audio_format_num >= 0 && gc->audio_format_num !=
audio_format_num)
continue;
- filter_num = gc->conf->filter_num_arg;
- if (filter_num >= num_filters(gc->audio_format_num))
+ if (gc->filter_num >= num_filters(audio_format_num))
continue;
- fn = fc->filter_nodes + filter_num;
+ fn = fc->filter_nodes + gc->filter_num;
activate_grab_client(gc, fn);
}
}
+static int check_gc_args(int argc, char **argv, struct grab_client *gc)
+{
+ int i, ret;
+
+ gc->audio_format_num = -1; /* default: grab any audio format */
+ for (i = 1; i < argc; i++) {
+ const char *arg = argv[i];
+ if (arg[0] != '-')
+ break;
+ if (!strcmp(arg, "--")) {
+ i++;
+ break;
+ }
+ if (!strncmp(arg, "-n=", 3)) {
+ ret = para_atoi32(arg + 3, &gc->filter_num);
+ if (ret < 0)
+ return ret;
+ if (gc->filter_num < 0)
+ return -E_BAD_GC_FILTER_NUM;
+ if (gc->filter_num >= max_num_filters())
+ return -E_BAD_GC_FILTER_NUM;
+ continue;
+ }
+ if (!strncmp(arg, "-m", 2)) {
+ if (*(arg + 3))
+ return -E_GC_SYNTAX;
+ switch(*(arg + 2)) {
+ case 's':
+ gc->mode = GM_SLOPPY;
+ continue;
+ case 'a':
+ gc->mode = GM_AGGRESSIVE;
+ continue;
+ case 'p':
+ gc->mode = GM_PEDANTIC;
+ continue;
+ default:
+ return -E_GC_SYNTAX;
+ }
+ }
+ if (!strcmp(arg, "-i")) {
+ gc->flags |= GF_INPUT_GRAB;
+ continue;
+ }
+ if (!strcmp(arg, "-o")) {
+ gc->flags |= GF_ONE_SHOT;
+ continue;
+ }
+ if (!strncmp(arg, "-f=", 3)) {
+ ret = get_audio_format_num(arg + 3);
+ if (ret < 0)
+ return ret;
+ gc->audio_format_num = ret;
+ continue;
+ }
+ return -E_GC_SYNTAX;
+ }
+ if (i != argc)
+ return -E_GC_SYNTAX;
+ return 1;
+}
+
/**
* Check the command line options and allocate a grab_client structure.
*
* \param fd The file descriptor of the client.
- * \param line The command line.
- * \param err Non-zero if an error occurred.
+ * \param argc Argument count.
+ * \param argv Argument vector.
*
* If the command line options given by \a argc and \a argv are valid.
* allocate a struct grab_client and initialize it with this valid
* configuration. Moreover, add the new grab client to the inactive list.
*
- * \return On success, this function returns a pointer to the newly created
- * struct. On errors, it returns NULL and sets \a err appropriately.
+ * \return Standard.
*
* \sa grab_client, inactive_grab_client_list, activate_grab_client,
* filter_node::callbacks.
*/
-/*
- * argc, argv get freed when com_grab() returns, so we have to make a
- * copy.
- */
-struct grab_client *grab_client_new(int fd, char *line, int *err)
+int grab_client_new(int fd, int argc, char **argv)
{
int ret;
struct grab_client *gc = para_calloc(sizeof(struct grab_client));
+ struct filter_node *fn;
- gc->conf = para_calloc(sizeof(struct grab_client_args_info));
-
- ret = grab_client_cmdline_parser_string(line, gc->conf, "grab");
- *err = -E_GC_SYNTAX;
- if (ret)
- goto err_out;
- *err = -E_GC_HELP_GIVEN;
- if (gc->conf->help_given)
+ ret = check_gc_args(argc, argv, gc);
+ if (ret < 0)
goto err_out;
- *err = -E_GC_VERSION_GIVEN;
- if (gc->conf->version_given)
- goto err_out;
- *err = check_gc_args(gc);
- if (*err < 0)
- goto err_out;
- if (gc->conf->input_grab_given) {
+ if (gc->flags & GF_INPUT_GRAB)
gc->fcb.input_cb = gc_write;
- gc->fcb.output_cb = NULL;
- } else {
+ else
gc->fcb.output_cb = gc_write;
- gc->fcb.input_cb = NULL;
- }
gc->fd = fd;
gc->fcb.close = gc_close;
- gc->fcb.data = gc;
- add_inactive_gc(gc);
- return gc;
+ fn = find_filter_node(gc->audio_format_num, gc->filter_num);
+ if (fn)
+ para_list_add(&gc->fcb.node, &fn->callbacks);
+ else
+ add_inactive_gc(gc);
+ return 1;
err_out:
- free(gc->conf);
free(gc);
- return NULL;
+ return ret;
}
/**
/** \file grab_client.h exported symbols from grab_client.c */
#include "config.h"
+
/**
- * handle blocking writes for the grab client fds
- *
- * - pedantic: close fd if write would block
- * - sloppy: ignore the data and do not write
- * - aggressive: write anyway (default)
- *
+ * How to handle blocking writes for the grab client fds.
*/
-enum grab_mode {GRAB_SLOPPY, GRAB_AGGRESSIVE, GRAB_PEDANTIC};
+enum grab_mode {
+ /** Ignore the data and do not write. */
+ GM_SLOPPY,
+ /** Write anyway (default). */
+ GM_AGGRESSIVE,
+ /** Close fd if write would block. */
+ GM_PEDANTIC,
+};
-/** describes one active grab client
+/** Flags specified as arguments to the grab command. */
+enum grab_flags {
+ /** Grab the filter input instead of its output. */
+ GF_INPUT_GRAB = 1,
+ /** Stop grabbing if audio file changes. */
+ GF_ONE_SHOT = 2,
+};
+
+/**
+ * Describes one active grab client.
*
- * \sa filter_callback, filter_node::callbacks
+ * \sa filter_callback, filter_node::callbacks.
*/
struct grab_client {
-/** the file descriptor to send the grabbed stream to */
+ /** The file descriptor to send the grabbed stream to. */
int fd;
-/** the command line options for this grab client */
- struct grab_client_args_info *conf;
-/** pedantic, sloppy, or aggressive, computed from command line */
- enum grab_mode mode;
-/** non-zero if the write() to \a fd failed */
+ /** Non-zero if the write() to \a fd failed. */
int error;
-/** the number of the desired audio format, computed from command line */
+ /** See \ref grab_mode. */
+ enum grab_mode mode;
+ /** Point of filter chain to grab. */
+ int32_t filter_num;
+ /** The number of the desired audio format. */
int audio_format_num;
-/** the callback data which gets attached to a suitable filter_node */
+ /** Flags given at the command line. */
+ enum grab_flags flags;
+ /** The callback data which gets attached to a suitable filter_node. */
struct filter_callback fcb;
-/** all grab clients belong either to a filter node or to the inactive list */
+ /** All grab clients belong either to a filter node or to the inactive list. */
struct list_head node;
-/** the number of command line options */
- int argc;
-/** pointers to the command line options */
- char **argv;
};
-__malloc struct grab_client *grab_client_new(int fd, char *line, int *err);
-void activate_inactive_grab_clients(int slot_num, int audio_format_num,
+int grab_client_new(int fd, int argc, char **argv);
+void activate_inactive_grab_clients(int audio_format_num,
struct filter_chain *fc);
void activate_grab_client(struct grab_client *gc, struct filter_node *fn);
void init_grabbing(void);