]> mj.ucw.cz Git - ursary.git/blob - pulse-ucw.c
Recover from PulseAudio failures. Important :)
[ursary.git] / pulse-ucw.c
1 /*
2  *      Glue between PulseAudio and LibUCW Mainloop
3  *
4  *      (c) 2014 Martin Mares <mj@ucw.cz>
5  */
6
7 #undef LOCAL_DEBUG
8
9 #include <ucw/lib.h>
10 #include <ucw/clists.h>
11 #include <ucw/mainloop.h>
12
13 #include <sys/poll.h>
14 #include <sys/time.h>
15
16 #include <pulse/pulseaudio.h>
17
18 #include "ursaryd.h"
19
20 struct pmain_io {
21   cnode n;
22   struct main_file f;
23   clist io_events;
24 };
25
26 static clist pmain_io_list;
27
28 struct pa_io_event {
29   cnode n;
30   cnode gc_n;
31   struct pmain_io *io;
32   pa_io_event_flags_t events;
33   pa_io_event_cb_t callback;
34   pa_io_event_destroy_cb_t destroy_callback;
35   void *userdata;
36 };
37
38 static clist pmain_io_gc_list;
39
40 static pa_io_event *pmain_io_new(pa_mainloop_api *api, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata);
41 static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events);
42 static void pmain_io_free(pa_io_event *e);
43 static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb);
44
45 struct pa_time_event {
46   cnode n;
47   struct main_timer t;
48   pa_time_event_cb_t callback;
49   pa_time_event_destroy_cb_t destroy_callback;
50   void *userdata;
51   struct timeval tv;
52 };
53
54 static clist pmain_time_gc_list;
55
56 static pa_time_event *pmain_time_new(pa_mainloop_api *api, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata);
57 static void pmain_time_restart(pa_time_event *e, const struct timeval *tv);
58 static void pmain_time_free(pa_time_event *e);
59 static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb);
60
61 struct pa_defer_event {
62   cnode n;
63   struct main_hook h;
64   pa_defer_event_cb_t callback;
65   pa_defer_event_destroy_cb_t destroy_callback;
66   void *userdata;
67 };
68
69 static clist pmain_defer_gc_list;
70
71 static pa_defer_event *pmain_defer_new(pa_mainloop_api *api, pa_defer_event_cb_t cb, void *userdata);
72 static void pmain_defer_enable(pa_defer_event *e, int b);
73 static void pmain_defer_free(pa_defer_event *e);
74 static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb);
75
76 static void pmain_quit(pa_mainloop_api *a, int retval);
77
78 static struct main_hook pmain_gc_hook;
79
80 static void pmain_schedule_gc(void);
81
82 struct pa_mainloop_api pmain_api = {
83   .io_new = pmain_io_new,
84   .io_enable = pmain_io_enable,
85   .io_free = pmain_io_free,
86   .io_set_destroy = pmain_io_set_destroy,
87
88   .time_new = pmain_time_new,
89   .time_restart = pmain_time_restart,
90   .time_free = pmain_time_free,
91   .time_set_destroy = pmain_time_set_destroy,
92
93   .defer_new = pmain_defer_new,
94   .defer_enable = pmain_defer_enable,
95   .defer_free = pmain_defer_free,
96   .defer_set_destroy = pmain_defer_set_destroy,
97
98   .quit = pmain_quit,
99 };
100
101 static struct pmain_io *pmain_get_io(int fd)
102 {
103   CLIST_FOR_EACH(struct pmain_io *, io, pmain_io_list)
104     if (io->f.fd == fd)
105       {
106         DBG("Pulse: Recycling IO master");
107         return io;
108       }
109
110   struct pmain_io *io = xmalloc_zero(sizeof(*io));
111   io->f.fd = fd;
112   io->f.data = io;
113   clist_add_tail(&pmain_io_list, &io->n);
114   clist_init(&io->io_events);
115   return io;
116 }
117
118 static pa_io_event *pmain_io_new(pa_mainloop_api *api UNUSED, int fd, pa_io_event_flags_t events, pa_io_event_cb_t cb, void *userdata)
119 {
120   struct pa_io_event *e = xmalloc_zero(sizeof(*e));
121   DBG("Pulse: Creating new IO %p for fd %u", e, fd);
122
123   e->io = pmain_get_io(fd);
124   e->callback = cb;
125   e->userdata = userdata;
126   clist_add_head(&e->io->io_events, &e->n);     // Do not call the new IO if created from another IO on the same fd
127   pmain_io_enable(e, events);
128   return e;
129 }
130
131 static int pmain_io_read(struct main_file *f)
132 {
133   struct pmain_io *io = f->data;
134   DBG("Pulse: fd %d ready for read", io->f.fd);
135
136   CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
137     if (e->events & PA_IO_EVENT_INPUT)
138       {
139         DBG("Pulse: Callback on IO %p", e);
140         e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_INPUT, e->userdata);
141       }
142
143   DBG("Pulse: fd %d read done", io->f.fd);
144   return HOOK_IDLE;
145 }
146
147 static int pmain_io_write(struct main_file *f)
148 {
149   struct pmain_io *io = f->data;
150   DBG("Pulse: fd %d ready for write", io->f.fd);
151
152   CLIST_FOR_EACH(struct pa_io_event *, e, io->io_events)
153     if (e->events & PA_IO_EVENT_OUTPUT)
154       {
155         DBG("Pulse: Callback on IO %p", e);
156         e->callback(&pmain_api, e, io->f.fd, PA_IO_EVENT_OUTPUT, e->userdata);
157       }
158
159   DBG("Pulse: fd %d write done", io->f.fd);
160   return HOOK_IDLE;
161 }
162
163 static void pmain_io_enable(pa_io_event *e, pa_io_event_flags_t events)
164 {
165   struct pmain_io *io = e->io;
166   DBG("Pulse: Changing IO event mask for IO %p on fd %d to %02x", e, io->f.fd, events);
167   e->events = events;
168
169   pa_io_event_flags_t mask = 0;
170   CLIST_FOR_EACH(struct pa_io_event *, f, io->io_events)
171     mask |= f->events;
172   DBG("Pulse: Recalculated IO mask for fd %d to %02x", io->f.fd, mask);
173
174   if (mask)
175     {
176       io->f.read_handler = (mask & PA_IO_EVENT_INPUT) ? pmain_io_read : NULL;
177       io->f.write_handler = (mask & PA_IO_EVENT_OUTPUT) ? pmain_io_write : NULL;
178       if (file_is_active(&io->f))
179         file_chg(&io->f);
180       else
181         file_add(&io->f);
182     }
183   else
184     file_del(&io->f);
185 }
186
187 static void pmain_io_free(pa_io_event *e)
188 {
189   DBG("Pulse: Deleting IO %p for fd %d", e, e->io->f.fd);
190   pmain_io_enable(e, 0);
191   clist_add_tail(&pmain_io_gc_list, &e->gc_n);
192   pmain_schedule_gc();
193 }
194
195 static void pmain_io_set_destroy(pa_io_event *e, pa_io_event_destroy_cb_t cb)
196 {
197   e->destroy_callback = cb;
198 }
199
200 static void pmain_time_handler(struct main_timer *t)
201 {
202   struct pa_time_event *e = t->data;
203   DBG("Pulse: Timer %p triggered", e);
204   timer_del(t);
205   e->callback(&pmain_api, e, &e->tv, e->userdata);
206   DBG("Pulse: Timer %p done", e);
207 }
208
209 static pa_time_event *pmain_time_new(pa_mainloop_api *api UNUSED, const struct timeval *tv, pa_time_event_cb_t cb, void *userdata)
210 {
211   struct pa_time_event *e = xmalloc_zero(sizeof(*e));
212   DBG("Pulse: Creating timer %p", e);
213   e->callback = cb;
214   e->userdata = userdata;
215   e->t.handler = pmain_time_handler;
216   e->t.data = e;
217   pmain_time_restart(e, tv);
218   return e;
219 }
220
221 static timestamp_t timeval_to_timestamp(const struct timeval *tv)
222 {
223   return 1000 * (timestamp_t) tv->tv_sec + tv->tv_usec / 1000;
224 }
225
226 static void pmain_time_restart(pa_time_event *e, const struct timeval *tv)
227 {
228   struct timeval now;
229   gettimeofday(&now, NULL);
230   timestamp_t ts_now = timeval_to_timestamp(&now);
231   timestamp_t ts_fire = timeval_to_timestamp(tv);
232   timestamp_t ts_delta = ts_fire - ts_now;
233   DBG("Pulse: Setting timer %p to %+d", e, (int) ts_delta);
234   timer_del(&e->t);
235   e->tv = *tv;
236   timer_add_rel(&e->t, ts_delta);
237 }
238
239 static void pmain_time_free(pa_time_event *e)
240 {
241   DBG("Pulse: Timer %p deleted", e);
242   timer_del(&e->t);
243   clist_add_tail(&pmain_time_gc_list, &e->n);
244   pmain_schedule_gc();
245 }
246
247 static void pmain_time_set_destroy(pa_time_event *e, pa_time_event_destroy_cb_t cb)
248 {
249   e->destroy_callback = cb;
250 }
251
252 static int pmain_defer_handler(struct main_hook *h)
253 {
254   struct pa_defer_event *e = h->data;
255   DBG("Pulse: Deferred event %p triggered", e);
256   e->callback(&pmain_api, e, e->userdata);
257   DBG("Pulse: Deferred event done");
258   return hook_is_active(&e->h) ? HOOK_RETRY : HOOK_IDLE;
259 }
260
261 static pa_defer_event *pmain_defer_new(pa_mainloop_api *api UNUSED, pa_defer_event_cb_t cb, void *userdata)
262 {
263   struct pa_defer_event *e = xmalloc_zero(sizeof(*e));
264   DBG("Pulse: Creating defer %p", e);
265   e->callback = cb;
266   e->userdata = userdata;
267   e->h.handler = pmain_defer_handler;
268   e->h.data = e;
269   pmain_defer_enable(e, 1);
270   return e;
271 }
272
273 static void pmain_defer_enable(pa_defer_event *e, int b)
274 {
275   DBG("Pulse: %sabling defer %p", (b ? "En" : "Dis"), e);
276   if (b)
277     hook_add(&e->h);
278   else
279     hook_del(&e->h);
280 }
281
282 static void pmain_defer_free(pa_defer_event *e)
283 {
284   DBG("Pulse: Deferred event %p deleted", e);
285   hook_del(&e->h);
286   clist_add_tail(&pmain_defer_gc_list, &e->n);
287   pmain_schedule_gc();
288 }
289
290 static void pmain_defer_set_destroy(pa_defer_event *e, pa_defer_event_destroy_cb_t cb)
291 {
292   e->destroy_callback = cb;
293 }
294
295 static void pmain_quit(pa_mainloop_api *a UNUSED, int retval UNUSED)
296 {
297   DBG("Pulse: Main loop quit not implemented");
298 }
299
300 static int pmain_gc_handler(struct main_hook *h)
301 {
302   DBG("Pulse: Garbage collector");
303   hook_del(h);
304
305   cnode *n;
306   while (n = clist_remove_head(&pmain_io_gc_list))
307     {
308       struct pa_io_event *ei = SKIP_BACK(struct pa_io_event, gc_n, n);
309       struct pmain_io *io = ei->io;
310       DBG("Pulse: GC of IO event %p on fd %d", ei, io->f.fd);
311       if (ei->destroy_callback)
312         ei->destroy_callback(&pmain_api, ei, ei->userdata);
313       clist_remove(&ei->n);
314       if (clist_empty(&io->io_events))
315         {
316           ASSERT(!file_is_active(&io->f));
317           DBG("Pulse: GC of IO master for fd %d", io->f.fd);
318           clist_remove(&io->n);
319           xfree(io);
320         }
321       xfree(ei);
322     }
323
324   struct pa_time_event *et;
325   while (et = (struct pa_time_event *) clist_remove_head(&pmain_time_gc_list))
326     {
327       DBG("Pulse: GC for timer %p", et);
328       if (et->destroy_callback)
329         et->destroy_callback(&pmain_api, et, et->userdata);
330       xfree(et);
331     }
332
333   struct pa_defer_event *ed;
334   while (ed = (struct pa_defer_event *) clist_remove_head(&pmain_defer_gc_list))
335     {
336       DBG("Pulse: GC for defer %p", ed);
337       if (ed->destroy_callback)
338         ed->destroy_callback(&pmain_api, ed, ed->userdata);
339       xfree(ed);
340     }
341
342   DBG("Pulse: Garbage collector done");
343   return HOOK_RETRY;
344 }
345
346 static void pmain_schedule_gc(void)
347 {
348   hook_add(&pmain_gc_hook);
349 }
350
351 void pmain_init(void)
352 {
353   clist_init(&pmain_io_list);
354   clist_init(&pmain_io_gc_list);
355   clist_init(&pmain_time_gc_list);
356   clist_init(&pmain_defer_gc_list);
357   pmain_gc_hook.handler = pmain_gc_handler;
358 }