]> code.delx.au - pulseaudio/blobdiff - src/modules/module-pipe-source.c
alsa-mixer: Make sure capture source and input source use right path
[pulseaudio] / src / modules / module-pipe-source.c
index f53f6a63b9e230cb9e1f32ddb2dc508311ed47f4..c50536efae80478299bdc89633345494b8361979 100644 (file)
@@ -1,11 +1,11 @@
-/* $Id$ */
-
 /***
   This file is part of PulseAudio.
 
 /***
   This file is part of PulseAudio.
 
+  Copyright 2004-2006 Lennart Poettering
+
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
   PulseAudio is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published
-  by the Free Software Foundation; either version 2 of the License,
+  by the Free Software Foundation; either version 2.1 of the License,
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
   or (at your option) any later version.
 
   PulseAudio is distributed in the hope that it will be useful, but
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <sys/stat.h>
 #include <stdio.h>
-#include <assert.h>
 #include <errno.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
 #include <errno.h>
 #include <string.h>
 #include <fcntl.h>
 #include <unistd.h>
 #include <limits.h>
+#include <sys/ioctl.h>
+
+#ifdef HAVE_SYS_FILIO_H
+#include <sys/filio.h>
+#endif
 
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/core-error.h>
 
 #include <pulse/xmalloc.h>
 
 #include <pulsecore/core-error.h>
-#include <pulsecore/iochannel.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
 #include <pulsecore/source.h>
 #include <pulsecore/module.h>
 #include <pulsecore/core-util.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/log.h>
+#include <pulsecore/thread.h>
+#include <pulsecore/thread-mq.h>
+#include <pulsecore/rtpoll.h>
+#include <pulsecore/poll.h>
 
 #include "module-pipe-source-symdef.h"
 
 
 #include "module-pipe-source-symdef.h"
 
-PA_MODULE_AUTHOR("Lennart Poettering")
-PA_MODULE_DESCRIPTION("UNIX pipe source")
-PA_MODULE_VERSION(PACKAGE_VERSION)
+PA_MODULE_AUTHOR("Lennart Poettering");
+PA_MODULE_DESCRIPTION("UNIX pipe source");
+PA_MODULE_VERSION(PACKAGE_VERSION);
+PA_MODULE_LOAD_ONCE(FALSE);
 PA_MODULE_USAGE(
         "source_name=<name for the source> "
 PA_MODULE_USAGE(
         "source_name=<name for the source> "
+        "source_properties=<properties for the source> "
         "file=<path of the FIFO> "
         "format=<sample format> "
         "file=<path of the FIFO> "
         "format=<sample format> "
-        "channels=<number of channels> "
         "rate=<sample rate> "
         "rate=<sample rate> "
-        "channel_map=<channel map>")
+        "channels=<number of channels> "
+        "channel_map=<channel map>");
 
 
-#define DEFAULT_FIFO_NAME "/tmp/music.input"
+#define DEFAULT_FILE_NAME "/tmp/music.input"
 #define DEFAULT_SOURCE_NAME "fifo_input"
 
 struct userdata {
     pa_core *core;
 #define DEFAULT_SOURCE_NAME "fifo_input"
 
 struct userdata {
     pa_core *core;
+    pa_module *module;
+    pa_source *source;
+
+    pa_thread *thread;
+    pa_thread_mq thread_mq;
+    pa_rtpoll *rtpoll;
 
     char *filename;
 
     char *filename;
+    int fd;
 
 
-    pa_source *source;
-    pa_iochannel *io;
-    pa_module *module;
-    pa_memchunk chunk;
+    pa_memchunk memchunk;
+
+    pa_rtpoll_item *rtpoll_item;
 };
 
 static const char* const valid_modargs[] = {
 };
 
 static const char* const valid_modargs[] = {
+    "source_name",
+    "source_properties",
     "file",
     "file",
+    "format",
     "rate",
     "channels",
     "rate",
     "channels",
-    "format",
-    "source_name",
     "channel_map",
     NULL
 };
 
     "channel_map",
     NULL
 };
 
-static void do_read(struct userdata *u) {
-    ssize_t r;
-    pa_memchunk chunk;
-    assert(u);
+static int source_process_msg(
+        pa_msgobject *o,
+        int code,
+        void *data,
+        int64_t offset,
+        pa_memchunk *chunk) {
 
 
-    if (!pa_iochannel_is_readable(u->io))
-        return;
+    struct userdata *u = PA_SOURCE(o)->userdata;
 
 
-    pa_module_set_used(u->module, pa_idxset_size(u->source->outputs));
+    switch (code) {
 
 
-    if (!u->chunk.memblock) {
-        u->chunk.memblock = pa_memblock_new(u->core->mempool, PIPE_BUF);
-        u->chunk.index = chunk.length = 0;
-    }
+        case PA_SOURCE_MESSAGE_GET_LATENCY: {
+            size_t n = 0;
 
 
-    assert(u->chunk.memblock && u->chunk.memblock->length > u->chunk.index);
-    if ((r = pa_iochannel_read(u->io, (uint8_t*) u->chunk.memblock->data + u->chunk.index, u->chunk.memblock->length - u->chunk.index)) <= 0) {
-        pa_log("read(): %s", pa_cstrerror(errno));
-        return;
-    }
+#ifdef FIONREAD
+            int l;
 
 
-    u->chunk.length = r;
-    pa_source_post(u->source, &u->chunk);
-    u->chunk.index += r;
+            if (ioctl(u->fd, FIONREAD, &l) >= 0 && l > 0)
+                n = (size_t) l;
+#endif
 
 
-    if (u->chunk.index >= u->chunk.memblock->length) {
-        u->chunk.index = u->chunk.length = 0;
-        pa_memblock_unref(u->chunk.memblock);
-        u->chunk.memblock = NULL;
+            *((pa_usec_t*) data) = pa_bytes_to_usec(n, &u->source->sample_spec);
+            return 0;
+        }
     }
     }
+
+    return pa_source_process_msg(o, code, data, offset, chunk);
 }
 
 }
 
-static void io_callback(PA_GCC_UNUSED pa_iochannel *io, void*userdata) {
+static void thread_func(void *userdata) {
     struct userdata *u = userdata;
     struct userdata *u = userdata;
-    assert(u);
-    do_read(u);
+    int read_type = 0;
+
+    pa_assert(u);
+
+    pa_log_debug("Thread starting up");
+
+    pa_thread_mq_install(&u->thread_mq);
+
+    for (;;) {
+        int ret;
+        struct pollfd *pollfd;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        /* Try to read some data and pass it on to the source driver */
+        if (u->source->thread_info.state == PA_SOURCE_RUNNING && pollfd->revents) {
+            ssize_t l;
+            void *p;
+
+            if (!u->memchunk.memblock) {
+                u->memchunk.memblock = pa_memblock_new(u->core->mempool, pa_pipe_buf(u->fd));
+                u->memchunk.index = u->memchunk.length = 0;
+            }
+
+            pa_assert(pa_memblock_get_length(u->memchunk.memblock) > u->memchunk.index);
+
+            p = pa_memblock_acquire(u->memchunk.memblock);
+            l = pa_read(u->fd, (uint8_t*) p + u->memchunk.index, pa_memblock_get_length(u->memchunk.memblock) - u->memchunk.index, &read_type);
+            pa_memblock_release(u->memchunk.memblock);
+
+            pa_assert(l != 0); /* EOF cannot happen, since we opened the fifo for both reading and writing */
+
+            if (l < 0) {
+
+                if (errno == EINTR)
+                    continue;
+                else if (errno != EAGAIN) {
+                    pa_log("Failed to read data from FIFO: %s", pa_cstrerror(errno));
+                    goto fail;
+                }
+
+            } else {
+
+                u->memchunk.length = (size_t) l;
+                pa_source_post(u->source, &u->memchunk);
+                u->memchunk.index += (size_t) l;
+
+                if (u->memchunk.index >= pa_memblock_get_length(u->memchunk.memblock)) {
+                    pa_memblock_unref(u->memchunk.memblock);
+                    pa_memchunk_reset(&u->memchunk);
+                }
+
+                pollfd->revents = 0;
+            }
+        }
+
+        /* Hmm, nothing to do. Let's sleep */
+        pollfd->events = (short) (u->source->thread_info.state == PA_SOURCE_RUNNING ? POLLIN : 0);
+
+        if ((ret = pa_rtpoll_run(u->rtpoll, TRUE)) < 0)
+            goto fail;
+
+        if (ret == 0)
+            goto finish;
+
+        pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+
+        if (pollfd->revents & ~POLLIN) {
+            pa_log("FIFO shutdown.");
+            goto fail;
+        }
+    }
+
+fail:
+    /* If this was no regular exit from the loop we have to continue
+     * processing messages until we received PA_MESSAGE_SHUTDOWN */
+    pa_asyncmsgq_post(u->thread_mq.outq, PA_MSGOBJECT(u->core), PA_CORE_MESSAGE_UNLOAD_MODULE, u->module, 0, NULL, NULL);
+    pa_asyncmsgq_wait_for(u->thread_mq.inq, PA_MESSAGE_SHUTDOWN);
+
+finish:
+    pa_log_debug("Thread shutting down");
 }
 
 }
 
-int pa__init(pa_core *c, pa_module*m) {
-    struct userdata *u = NULL;
+int pa__init(pa_module*m) {
+    struct userdata *u;
     struct stat st;
     struct stat st;
-    const char *p;
-    int fd = -1;
     pa_sample_spec ss;
     pa_channel_map map;
     pa_sample_spec ss;
     pa_channel_map map;
-    pa_modargs *ma = NULL;
-    char *t;
+    pa_modargs *ma;
+    struct pollfd *pollfd;
+    pa_source_new_data data;
 
 
-    assert(c && m);
+    pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
-        pa_log("failed to parse module arguments");
+        pa_log("failed to parse module arguments.");
         goto fail;
     }
 
         goto fail;
     }
 
-    ss = c->default_sample_spec;
+    ss = m->core->default_sample_spec;
+    map = m->core->default_channel_map;
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
         pa_log("invalid sample format specification or channel map");
         goto fail;
     }
 
     if (pa_modargs_get_sample_spec_and_channel_map(ma, &ss, &map, PA_CHANNEL_MAP_DEFAULT) < 0) {
         pa_log("invalid sample format specification or channel map");
         goto fail;
     }
 
-    mkfifo(p = pa_modargs_get_value(ma, "file", DEFAULT_FIFO_NAME), 0777);
+    m->userdata = u = pa_xnew0(struct userdata, 1);
+    u->core = m->core;
+    u->module = m;
+    pa_memchunk_reset(&u->memchunk);
+    u->rtpoll = pa_rtpoll_new();
+    pa_thread_mq_init(&u->thread_mq, m->core->mainloop, u->rtpoll);
+
+    u->filename = pa_runtime_path(pa_modargs_get_value(ma, "file", DEFAULT_FILE_NAME));
 
 
-    if ((fd = open(p, O_RDWR)) < 0) {
-        pa_log("open('%s'): %s", p, pa_cstrerror(errno));
+    mkfifo(u->filename, 0666);
+    if ((u->fd = pa_open_cloexec(u->filename, O_RDWR, 0)) < 0) {
+        pa_log("open('%s'): %s", u->filename, pa_cstrerror(errno));
         goto fail;
     }
 
         goto fail;
     }
 
-    pa_fd_set_cloexec(fd, 1);
+    pa_make_fd_nonblock(u->fd);
 
 
-    if (fstat(fd, &st) < 0) {
-        pa_log("fstat('%s'): %s", p, pa_cstrerror(errno));
+    if (fstat(u->fd, &st) < 0) {
+        pa_log("fstat('%s'): %s",u->filename, pa_cstrerror(errno));
         goto fail;
     }
 
     if (!S_ISFIFO(st.st_mode)) {
         goto fail;
     }
 
     if (!S_ISFIFO(st.st_mode)) {
-        pa_log("'%s' is not a FIFO.", p);
+        pa_log("'%s' is not a FIFO.", u->filename);
         goto fail;
     }
 
         goto fail;
     }
 
-    u = pa_xmalloc0(sizeof(struct userdata));
+    pa_source_new_data_init(&data);
+    data.driver = __FILE__;
+    data.module = m;
+    pa_source_new_data_set_name(&data, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME));
+    pa_proplist_sets(data.proplist, PA_PROP_DEVICE_STRING, u->filename);
+    pa_proplist_setf(data.proplist, PA_PROP_DEVICE_DESCRIPTION, "Unix FIFO source %s", u->filename);
+    pa_source_new_data_set_sample_spec(&data, &ss);
+    pa_source_new_data_set_channel_map(&data, &map);
+
+    if (pa_modargs_get_proplist(ma, "source_properties", data.proplist, PA_UPDATE_REPLACE) < 0) {
+        pa_log("Invalid properties");
+        pa_source_new_data_done(&data);
+        goto fail;
+    }
 
 
-    u->filename = pa_xstrdup(p);
-    u->core = c;
+    u->source = pa_source_new(m->core, &data, PA_SOURCE_LATENCY);
+    pa_source_new_data_done(&data);
 
 
-    if (!(u->source = pa_source_new(c, __FILE__, pa_modargs_get_value(ma, "source_name", DEFAULT_SOURCE_NAME), 0, &ss, &map))) {
-        pa_log("failed to create source.");
+    if (!u->source) {
+        pa_log("Failed to create source.");
         goto fail;
     }
         goto fail;
     }
+
+    u->source->parent.process_msg = source_process_msg;
     u->source->userdata = u;
     u->source->userdata = u;
-    pa_source_set_owner(u->source, m);
-    pa_source_set_description(u->source, t = pa_sprintf_malloc("Unix FIFO source '%s'", p));
-    pa_xfree(t);
 
 
-    u->io = pa_iochannel_new(c->mainloop, fd, -1);
-    assert(u->io);
-    pa_iochannel_set_callback(u->io, io_callback, u);
+    pa_source_set_asyncmsgq(u->source, u->thread_mq.inq);
+    pa_source_set_rtpoll(u->source, u->rtpoll);
+    pa_source_set_fixed_latency(u->source, pa_bytes_to_usec(PIPE_BUF, &u->source->sample_spec));
 
 
-    u->chunk.memblock = NULL;
-    u->chunk.index = u->chunk.length = 0;
+    u->rtpoll_item = pa_rtpoll_item_new(u->rtpoll, PA_RTPOLL_NEVER, 1);
+    pollfd = pa_rtpoll_item_get_pollfd(u->rtpoll_item, NULL);
+    pollfd->fd = u->fd;
+    pollfd->events = pollfd->revents = 0;
 
 
-    u->module = m;
-    m->userdata = u;
+    if (!(u->thread = pa_thread_new("pipe-source", thread_func, u))) {
+        pa_log("Failed to create thread.");
+        goto fail;
+    }
+
+    pa_source_put(u->source);
 
     pa_modargs_free(ma);
 
 
     pa_modargs_free(ma);
 
@@ -192,31 +310,57 @@ fail:
     if (ma)
         pa_modargs_free(ma);
 
     if (ma)
         pa_modargs_free(ma);
 
-    if (fd >= 0)
-        close(fd);
-
-    pa__done(c, m);
+    pa__done(m);
 
     return -1;
 }
 
 
     return -1;
 }
 
-void pa__done(pa_core *c, pa_module*m) {
+int pa__get_n_used(pa_module *m) {
+    struct userdata *u;
+
+    pa_assert(m);
+    pa_assert_se(u = m->userdata);
+
+    return pa_source_linked_by(u->source);
+}
+
+void pa__done(pa_module*m) {
     struct userdata *u;
     struct userdata *u;
-    assert(c && m);
+
+    pa_assert(m);
 
     if (!(u = m->userdata))
         return;
 
 
     if (!(u = m->userdata))
         return;
 
-    if (u->chunk.memblock)
-        pa_memblock_unref(u->chunk.memblock);
+    if (u->source)
+        pa_source_unlink(u->source);
+
+    if (u->thread) {
+        pa_asyncmsgq_send(u->thread_mq.inq, NULL, PA_MESSAGE_SHUTDOWN, NULL, 0, NULL);
+        pa_thread_free(u->thread);
+    }
+
+    pa_thread_mq_done(&u->thread_mq);
+
+    if (u->source)
+        pa_source_unref(u->source);
 
 
-    pa_source_disconnect(u->source);
-    pa_source_unref(u->source);
-    pa_iochannel_free(u->io);
+    if (u->memchunk.memblock)
+        pa_memblock_unref(u->memchunk.memblock);
+
+    if (u->rtpoll_item)
+        pa_rtpoll_item_free(u->rtpoll_item);
+
+    if (u->rtpoll)
+        pa_rtpoll_free(u->rtpoll);
+
+    if (u->filename) {
+        unlink(u->filename);
+        pa_xfree(u->filename);
+    }
 
 
-    assert(u->filename);
-    unlink(u->filename);
-    pa_xfree(u->filename);
+    if (u->fd >= 0)
+        pa_assert_se(pa_close(u->fd) == 0);
 
     pa_xfree(u);
 }
 
     pa_xfree(u);
 }