]> code.delx.au - pulseaudio/blobdiff - src/modules/rtp/module-rtp-recv.c
port module-rtp-send.c to lock-free core
[pulseaudio] / src / modules / rtp / module-rtp-recv.c
index 62ef561f1f01138fead240126b1c800b56516c20..f5e9c3c29e71ec709b496542d102440e75fec002 100644 (file)
@@ -24,7 +24,6 @@
 #include <config.h>
 #endif
 
-#include <assert.h>
 #include <stdio.h>
 #include <sys/socket.h>
 #include <netinet/in.h>
 #include <pulsecore/modargs.h>
 #include <pulsecore/namereg.h>
 #include <pulsecore/sample-util.h>
+#include <pulsecore/macro.h>
+#include <pulsecore/atomic.h>
+#include <pulsecore/rtclock.h>
+#include <pulsecore/atomic.h>
 
 #include "module-rtp-recv-symdef.h"
 
@@ -66,7 +69,7 @@ PA_MODULE_USAGE(
 #define DEFAULT_SAP_ADDRESS "224.0.0.56"
 #define MEMBLOCKQ_MAXLENGTH (1024*170)
 #define MAX_SESSIONS 16
-#define DEATH_TIMEOUT 20000000
+#define DEATH_TIMEOUT 20
 
 static const char* const valid_modargs[] = {
     "sink",
@@ -76,102 +79,113 @@ static const char* const valid_modargs[] = {
 
 struct session {
     struct userdata *userdata;
+    PA_LLIST_FIELDS(struct session);
 
     pa_sink_input *sink_input;
     pa_memblockq *memblockq;
 
-    pa_time_event *death_event;
-
-    int first_packet;
+    pa_bool_t first_packet;
     uint32_t ssrc;
     uint32_t offset;
 
     struct pa_sdp_info sdp_info;
 
     pa_rtp_context rtp_context;
-    pa_io_event* rtp_event;
+
+    pa_rtpoll_item *rtpoll_item;
+
+    pa_atomic_t timestamp;
 };
 
 struct userdata {
     pa_module *module;
-    pa_core *core;
 
     pa_sap_context sap_context;
     pa_io_event* sap_event;
 
-    pa_hashmap *by_origin;
+    pa_time_event *check_death_event;
 
     char *sink_name;
 
+    PA_LLIST_HEAD(struct session, sessions);
+    pa_hashmap *by_origin;
     int n_sessions;
 };
 
-static void session_free(struct session *s, int from_hash);
+static void session_free(struct session *s);
 
-static int sink_input_peek(pa_sink_input *i, pa_memchunk *chunk) {
-    struct session *s;
-    assert(i);
-    s = i->userdata;
+/* Called from I/O thread context */
+static int sink_input_process_msg(pa_msgobject *o, int code, void *data, int64_t offset, pa_memchunk *chunk) {
+    struct session *s = PA_SINK_INPUT(o)->userdata;
 
-    return pa_memblockq_peek(s->memblockq, chunk);
+    switch (code) {
+        case PA_SINK_INPUT_MESSAGE_GET_LATENCY:
+            *((pa_usec_t*) data) = pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &s->sink_input->sample_spec);
+
+            /* Fall through, the default handler will add in the extra
+             * latency added by the resampler */
+            break;
+    }
+
+    return pa_sink_input_process_msg(o, code, data, offset, chunk);
 }
 
-static void sink_input_drop(pa_sink_input *i, const pa_memchunk *chunk, size_t length) {
+/* Called from I/O thread context */
+static int sink_input_peek(pa_sink_input *i, size_t length, pa_memchunk *chunk) {
     struct session *s;
-    assert(i);
-    s = i->userdata;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
 
-    pa_memblockq_drop(s->memblockq, chunk, length);
+    return pa_memblockq_peek(s->memblockq, chunk);
 }
 
-static void sink_input_kill(pa_sink_input* i) {
+/* Called from I/O thread context */
+static void sink_input_drop(pa_sink_input *i, size_t length) {
     struct session *s;
-    assert(i);
-    s = i->userdata;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
 
-    session_free(s, 1);
+    pa_memblockq_drop(s->memblockq, length);
 }
 
-static pa_usec_t sink_input_get_latency(pa_sink_input *i) {
+/* Called from main context */
+static void sink_input_kill(pa_sink_input* i) {
     struct session *s;
-    assert(i);
-    s = i->userdata;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
 
-    return pa_bytes_to_usec(pa_memblockq_get_length(s->memblockq), &i->sample_spec);
+    session_free(s);
 }
 
-static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event_flags_t flags, void *userdata) {
-    struct session *s = userdata;
+/* Called from I/O thread context */
+static int rtpoll_work_cb(pa_rtpoll_item *i) {
     pa_memchunk chunk;
     int64_t k, j, delta;
-    struct timeval tv;
+    struct timespec now;
+    struct session *s;
 
-    assert(m);
-    assert(e);
-    assert(s);
-    assert(fd == s->rtp_context.fd);
-    assert(flags == PA_IO_EVENT_INPUT);
+    pa_assert_se(s = pa_rtpoll_item_get_userdata(i));
 
-    if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->core->mempool) < 0)
-        return;
+    if (pa_rtp_recv(&s->rtp_context, &chunk, s->userdata->module->core->mempool) < 0)
+        return 0;
 
     if (s->sdp_info.payload != s->rtp_context.payload) {
         pa_memblock_unref(chunk.memblock);
-        return;
+        return 0;
     }
 
     if (!s->first_packet) {
-        s->first_packet = 1;
+        s->first_packet = TRUE;
 
         s->ssrc = s->rtp_context.ssrc;
         s->offset = s->rtp_context.timestamp;
 
-        if (s->ssrc == s->userdata->core->cookie)
-            pa_log_warn("WARNING! Detected RTP packet loop!");
+        if (s->ssrc == s->userdata->module->core->cookie)
+            pa_log_warn("Detected RTP packet loop!");
     } else {
         if (s->ssrc != s->rtp_context.ssrc) {
             pa_memblock_unref(chunk.memblock);
-            return;
+            return 0;
         }
     }
 
@@ -197,26 +211,49 @@ static void rtp_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
 
     pa_memblock_unref(chunk.memblock);
 
-    /* Reset death timer */
-    pa_gettimeofday(&tv);
-    pa_timeval_add(&tv, DEATH_TIMEOUT);
-    m->time_restart(s->death_event, &tv);
+    pa_rtclock_get(&now);
+    pa_atomic_store(&s->timestamp, now.tv_sec);
+
+    return 1;
 }
 
-static void death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *tv, void *userdata) {
-    struct session *s = userdata;
+/* Called from I/O thread context */
+static void sink_input_attach(pa_sink_input *i) {
+    struct session *s;
+    struct pollfd *p;
+    
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
+
+    pa_assert(!s->rtpoll_item);
+    s->rtpoll_item = pa_rtpoll_item_new(i->sink->rtpoll, PA_RTPOLL_LATE, 1);
 
-    assert(m);
-    assert(t);
-    assert(tv);
-    assert(s);
+    p = pa_rtpoll_item_get_pollfd(s->rtpoll_item, NULL);
+    p->fd = s->rtp_context.fd;
+    p->events = POLLIN;
+    p->revents = 0;
+
+    pa_rtpoll_item_set_work_callback(s->rtpoll_item, rtpoll_work_cb);
+    pa_rtpoll_item_set_userdata(s->rtpoll_item, s);
+}
+
+/* Called from I/O thread context */
+static void sink_input_detach(pa_sink_input *i) {
+    struct session *s;
+    pa_sink_input_assert_ref(i);
+    pa_assert_se(s = i->userdata);
 
-    session_free(s, 1);
+    pa_assert(s->rtpoll_item);
+    pa_rtpoll_item_free(s->rtpoll_item);
+    s->rtpoll_item = NULL;
 }
 
 static int mcast_socket(const struct sockaddr* sa, socklen_t salen) {
     int af, fd = -1, r, one;
 
+    pa_assert(sa);
+    pa_assert(salen > 0);
+
     af = sa->sa_family;
     if ((fd = socket(af, SOCK_DGRAM, 0)) < 0) {
         pa_log("Failed to create socket: %s", pa_cstrerror(errno));
@@ -262,27 +299,34 @@ fail:
 
 static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_info) {
     struct session *s = NULL;
-    struct timeval tv;
     char *c;
     pa_sink *sink;
     int fd = -1;
     pa_memblock *silence;
     pa_sink_input_new_data data;
+    struct timespec now;
+
+    pa_assert(u);
+    pa_assert(sdp_info);
 
     if (u->n_sessions >= MAX_SESSIONS) {
-        pa_log("session limit reached.");
+        pa_log("Session limit reached.");
         goto fail;
     }
 
-    if (!(sink = pa_namereg_get(u->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
-        pa_log("sink does not exist.");
+    if (!(sink = pa_namereg_get(u->module->core, u->sink_name, PA_NAMEREG_SINK, 1))) {
+        pa_log("Sink does not exist.");
         goto fail;
     }
 
     s = pa_xnew0(struct session, 1);
     s->userdata = u;
-    s->first_packet = 0;
+    s->first_packet = FALSE;
     s->sdp_info = *sdp_info;
+    s->rtpoll_item = NULL;
+
+    pa_rtclock_get(&now);
+    pa_atomic_store(&s->timestamp, now.tv_sec);
 
     if ((fd = mcast_socket((const struct sockaddr*) &sdp_info->sa, sdp_info->salen)) < 0)
         goto fail;
@@ -299,25 +343,27 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
     data.module = u->module;
     pa_sink_input_new_data_set_sample_spec(&data, &sdp_info->sample_spec);
 
-    s->sink_input = pa_sink_input_new(u->core, &data, 0);
+    s->sink_input = pa_sink_input_new(u->module->core, &data, 0);
     pa_xfree(c);
 
     if (!s->sink_input) {
-        pa_log("failed to create sink input.");
+        pa_log("Failed to create sink input.");
         goto fail;
     }
 
     s->sink_input->userdata = s;
 
+    s->sink_input->parent.process_msg = sink_input_process_msg;
     s->sink_input->peek = sink_input_peek;
     s->sink_input->drop = sink_input_drop;
     s->sink_input->kill = sink_input_kill;
-    s->sink_input->get_latency = sink_input_get_latency;
+    s->sink_input->attach = sink_input_attach;
+    s->sink_input->detach = sink_input_detach;
 
-    silence = pa_silence_memblock_new(s->userdata->core->mempool,
-                                      &s->sink_input->sample_spec,
-                                      (pa_bytes_per_second(&s->sink_input->sample_spec)/128/pa_frame_size(&s->sink_input->sample_spec))*
-                                      pa_frame_size(&s->sink_input->sample_spec));
+    silence = pa_silence_memblock_new(
+            s->userdata->module->core->mempool,
+            &s->sink_input->sample_spec,
+            pa_frame_align(pa_bytes_per_second(&s->sink_input->sample_spec)/128, &s->sink_input->sample_spec));
 
     s->memblockq = pa_memblockq_new(
             0,
@@ -330,53 +376,43 @@ static struct session *session_new(struct userdata *u, const pa_sdp_info *sdp_in
 
     pa_memblock_unref(silence);
 
-    s->rtp_event = u->core->mainloop->io_new(u->core->mainloop, fd, PA_IO_EVENT_INPUT, rtp_event_cb, s);
-
-    pa_gettimeofday(&tv);
-    pa_timeval_add(&tv, DEATH_TIMEOUT);
-    s->death_event = u->core->mainloop->time_new(u->core->mainloop, &tv, death_event_cb, s);
+    pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
 
     pa_hashmap_put(s->userdata->by_origin, s->sdp_info.origin, s);
+    u->n_sessions++;
+    PA_LLIST_PREPEND(struct session, s->userdata->sessions, s);
 
-    pa_rtp_context_init_recv(&s->rtp_context, fd, pa_frame_size(&s->sdp_info.sample_spec));
-
-    pa_log_info("Found new session '%s'", s->sdp_info.session_name);
+    pa_sink_input_put(s->sink_input);
 
-    u->n_sessions++;
+    pa_log_info("New session '%s'", s->sdp_info.session_name);
 
     return s;
 
 fail:
-    if (s) {
-        if (fd >= 0)
-            close(fd);
-
-        pa_xfree(s);
-    }
+    pa_xfree(s);
 
+    if (fd >= 0)
+        pa_close(fd);
+    
     return NULL;
 }
 
-static void session_free(struct session *s, int from_hash) {
-    assert(s);
+static void session_free(struct session *s) {
+    pa_assert(s);
 
     pa_log_info("Freeing session '%s'", s->sdp_info.session_name);
 
-    s->userdata->core->mainloop->time_free(s->death_event);
-    s->userdata->core->mainloop->io_free(s->rtp_event);
-
-    if (from_hash)
-        pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin);
-
-    pa_sink_input_disconnect(s->sink_input);
+    pa_sink_input_unlink(s->sink_input);
     pa_sink_input_unref(s->sink_input);
 
     pa_memblockq_free(s->memblockq);
     pa_sdp_info_destroy(&s->sdp_info);
     pa_rtp_context_destroy(&s->rtp_context);
 
-    assert(s->userdata->n_sessions >= 1);
+    PA_LLIST_REMOVE(struct session, s->userdata->sessions, s);
+    pa_assert(s->userdata->n_sessions >= 1);
     s->userdata->n_sessions--;
+    pa_hashmap_remove(s->userdata->by_origin, s->sdp_info.origin);
 
     pa_xfree(s);
 }
@@ -387,11 +423,11 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
     pa_sdp_info info;
     struct session *s;
 
-    assert(m);
-    assert(e);
-    assert(u);
-    assert(fd == u->sap_context.fd);
-    assert(flags == PA_IO_EVENT_INPUT);
+    pa_assert(m);
+    pa_assert(e);
+    pa_assert(u);
+    pa_assert(fd == u->sap_context.fd);
+    pa_assert(flags == PA_IO_EVENT_INPUT);
 
     if (pa_sap_recv(&u->sap_context, &goodbye) < 0)
         return;
@@ -402,7 +438,7 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
     if (goodbye) {
 
         if ((s = pa_hashmap_get(u->by_origin, info.origin)))
-            session_free(s, 1);
+            session_free(s);
 
         pa_sdp_info_destroy(&info);
     } else {
@@ -412,18 +448,47 @@ static void sap_event_cb(pa_mainloop_api *m, pa_io_event *e, int fd, pa_io_event
                 pa_sdp_info_destroy(&info);
 
         } else {
-            struct timeval tv;
-
-            pa_gettimeofday(&tv);
-            pa_timeval_add(&tv, DEATH_TIMEOUT);
-            m->time_restart(s->death_event, &tv);
-
+            struct timespec now;
+            pa_rtclock_get(&now);
+            pa_atomic_store(&s->timestamp, now.tv_sec);
+            
             pa_sdp_info_destroy(&info);
         }
     }
 }
 
-int pa__init(pa_core *c, pa_module*m) {
+static void check_death_event_cb(pa_mainloop_api *m, pa_time_event *t, const struct timeval *ptv, void *userdata) {
+    struct session *s, *n;
+    struct userdata *u = userdata;
+    struct timespec now;
+    struct timeval tv;
+
+    pa_assert(m);
+    pa_assert(t);
+    pa_assert(ptv);
+    pa_assert(s);
+
+    pa_rtclock_get(&now);
+
+    pa_log_debug("Checking for dead streams ...");
+    
+    for (s = u->sessions; s; s = n) {
+        int k;
+        n = s->next;
+
+        k = pa_atomic_load(&s->timestamp);
+
+        if (k + DEATH_TIMEOUT < now.tv_sec)
+            session_free(s);
+    }
+
+    /* Restart timer */
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, DEATH_TIMEOUT*PA_USEC_PER_SEC);
+    m->time_restart(t, &tv);
+}
+
+int pa__init(pa_module*m) {
     struct userdata *u;
     pa_modargs *ma = NULL;
     struct sockaddr_in sa4;
@@ -432,9 +497,9 @@ int pa__init(pa_core *c, pa_module*m) {
     socklen_t salen;
     const char *sap_address;
     int fd = -1;
-
-    assert(c);
-    assert(m);
+    struct timeval tv;
+    
+    pa_assert(m);
 
     if (!(ma = pa_modargs_new(m->argument, valid_modargs))) {
         pa_log("failed to parse module arguments");
@@ -454,7 +519,7 @@ int pa__init(pa_core *c, pa_module*m) {
         sa = (struct sockaddr*) &sa4;
         salen = sizeof(sa4);
     } else {
-        pa_log("invalid SAP address '%s'", sap_address);
+        pa_log("Invalid SAP address '%s'", sap_address);
         goto fail;
     }
 
@@ -464,16 +529,19 @@ int pa__init(pa_core *c, pa_module*m) {
     u = pa_xnew(struct userdata, 1);
     m->userdata = u;
     u->module = m;
-    u->core = c;
     u->sink_name = pa_xstrdup(pa_modargs_get_value(ma, "sink", NULL));
     u->n_sessions = 0;
 
-    u->sap_event = c->mainloop->io_new(c->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
+    u->sap_event = m->core->mainloop->io_new(m->core->mainloop, fd, PA_IO_EVENT_INPUT, sap_event_cb, u);
 
     u->by_origin = pa_hashmap_new(pa_idxset_string_hash_func, pa_idxset_string_compare_func);
 
     pa_sap_context_init_recv(&u->sap_context, fd);
 
+    pa_gettimeofday(&tv);
+    pa_timeval_add(&tv, DEATH_TIMEOUT * PA_USEC_PER_SEC);
+    u->check_death_event = m->core->mainloop->time_new(m->core->mainloop, &tv, check_death_event_cb, u);
+    
     pa_modargs_free(ma);
 
     return 0;
@@ -483,27 +551,34 @@ fail:
         pa_modargs_free(ma);
 
     if (fd >= 0)
-        close(fd);
+        pa_close(fd);
 
     return -1;
 }
 
-static void free_func(void *p, PA_GCC_UNUSED void *userdata) {
-    session_free(p, 0);
-}
-
-void pa__done(pa_core *c, pa_module*m) {
+void pa__done(pa_module*m) {
     struct userdata *u;
-    assert(c);
-    assert(m);
+    struct session *s;
+    
+    pa_assert(m);
 
     if (!(u = m->userdata))
         return;
 
-    c->mainloop->io_free(u->sap_event);
+    if (u->sap_event)
+        m->core->mainloop->io_free(u->sap_event);
+
+    if (u->check_death_event)
+        m->core->mainloop->time_free(u->check_death_event);
+
     pa_sap_context_destroy(&u->sap_context);
 
-    pa_hashmap_free(u->by_origin, free_func, NULL);
+    if (u->by_origin) {
+        while ((s = pa_hashmap_get_first(u->by_origin)))
+            session_free(s);
+        
+        pa_hashmap_free(u->by_origin, NULL, NULL);
+    }
 
     pa_xfree(u->sink_name);
     pa_xfree(u);