]> git.tuebingen.mpg.de Git - paraslash.git/blob - ao_write.c
ao_write: Fix spurious segfault.
[paraslash.git] / ao_write.c
1 /*
2  * Copyright (C) 2011-2014 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file ao_write.c Paraslash's libao output plugin. */
8
9 #include <pthread.h>
10 #include <ao/ao.h>
11 #include <regex.h>
12
13 #include "para.h"
14 #include "fd.h"
15 #include "string.h"
16 #include "list.h"
17 #include "sched.h"
18 #include "ggo.h"
19 #include "buffer_tree.h"
20 #include "write.h"
21 #include "write_common.h"
22 #include "ao_write.cmdline.h"
23 #include "error.h"
24
25 struct private_aow_data {
26         ao_device *dev;
27         int bytes_per_frame;
28
29         pthread_t thread;
30         pthread_attr_t attr;
31         pthread_mutex_t mutex;
32         pthread_cond_t data_available;
33         struct btr_node *thread_btrn;
34 };
35
36 static void aow_close(struct writer_node *wn)
37 {
38         struct private_aow_data *pawd = wn->private_data;
39
40         if (!pawd)
41                 return;
42         ao_close(pawd->dev);
43         free(pawd);
44         wn->private_data = NULL;
45         ao_shutdown();
46 }
47
48 static void aow_pre_select(struct sched *s, struct task *t)
49 {
50         struct writer_node *wn = container_of(t, struct writer_node, task);
51         struct private_aow_data *pawd = wn->private_data;
52         int ret;
53
54         if (pawd)
55                 pthread_mutex_lock(&pawd->mutex);
56         ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
57         if (pawd)
58                 pthread_mutex_unlock(&pawd->mutex);
59
60         if (ret == 0)
61                 return;
62         sched_min_delay(s);
63 }
64
65 static int aow_set_sample_format(unsigned sample_rate, unsigned channels,
66                 int sample_format, ao_sample_format *result)
67 {
68         memset(result, 0, sizeof(*result));
69         switch (sample_format) {
70                 case SF_U8:
71                 case SF_U16_LE:
72                 case SF_U16_BE:
73                         return -E_AO_BAD_SAMPLE_FORMAT;
74                 case SF_S8:
75                         /* no need to set byte_format */
76                         result->bits = 8;
77                         break;
78                 case SF_S16_LE:
79                         result->bits = 16;
80                         result->byte_format = AO_FMT_LITTLE;
81                         break;
82                 case SF_S16_BE:
83                         result->bits = 16;
84                         result->byte_format = AO_FMT_BIG;
85                         break;
86                 default:
87                         PARA_EMERG_LOG("bug: invalid sample format\n");
88                         exit(EXIT_FAILURE);
89         }
90         result->channels = channels;
91         result->rate = sample_rate;
92         return 1;
93 }
94
95 static int aow_open_device(int id, ao_sample_format *asf, ao_option *options,
96                 ao_device **result)
97 {
98         const char *msg;
99         ao_device *dev = ao_open_live(id, asf, options);
100
101         if (dev) {
102                 *result = dev;
103                 return 1;
104         }
105         switch (errno) {
106                 case AO_ENODRIVER:
107                         msg = "No driver corresponds to driver_id";
108                         break;
109                 case AO_ENOTLIVE:
110                         msg = "This driver is not a live output device";
111                         break;
112                 case AO_EBADOPTION:
113                         msg = "A valid option key has an invalid value";
114                         break;
115                 case AO_EOPENDEVICE:
116                         msg = "Cannot open the device";
117                         break;
118                 case AO_EFAIL:
119                         msg = "General libao error";
120                         break;
121                 default:
122                         msg = "Unknown ao error";
123                         break;
124         }
125         PARA_ERROR_LOG("%s\n", msg);
126         return -E_AO_OPEN_LIVE;
127 }
128
129 static int aow_init(struct writer_node *wn, unsigned sample_rate,
130                 unsigned channels, int sample_format)
131 {
132         int id, ret, i;
133         ao_option *aoo = NULL;
134         ao_sample_format asf;
135         ao_info *info;
136         struct private_aow_data *pawd = para_malloc(sizeof(*pawd));
137         struct ao_write_args_info *conf = wn->conf;
138
139         ao_initialize();
140         if (conf->driver_given) {
141                 ret = -E_AO_BAD_DRIVER;
142                 id = ao_driver_id(conf->driver_arg);
143         } else {
144                 ret = -E_AO_DEFAULT_DRIVER;
145                 id = ao_default_driver_id();
146         }
147         if (id < 0)
148                 goto fail;
149         info = ao_driver_info(id);
150         assert(info && info->short_name);
151         if (info->type == AO_TYPE_FILE) {
152                 ret = -E_AO_FILE_NOT_SUPP;
153                 goto fail;
154         }
155         PARA_INFO_LOG("using %s driver\n", info->short_name);
156         for (i = 0; i < conf->ao_option_given; i++) {
157                 char *o = para_strdup(conf->ao_option_arg[i]), *value;
158
159                 ret = -E_AO_BAD_OPTION;
160                 value = strchr(o, ':');
161                 if (!value) {
162                         free(o);
163                         goto fail;
164                 }
165                 *value = '\0';
166                 value++;
167                 PARA_INFO_LOG("appending option: key=%s, value=%s\n", o, value);
168                 ret = ao_append_option(&aoo, o, value);
169                 free(o);
170                 if (ret == 0) {
171                         ret = -E_AO_APPEND_OPTION;
172                         goto fail;
173                 }
174         }
175         ret = aow_set_sample_format(sample_rate, channels, sample_format, &asf);
176         if (ret < 0)
177                 goto fail;
178         if (sample_format == SF_S8 || sample_format == SF_U8)
179                 pawd->bytes_per_frame = channels;
180         else
181                 pawd->bytes_per_frame = channels * 2;
182         ret = aow_open_device(id, &asf, aoo, &pawd->dev);
183         if (ret < 0)
184                 goto fail;
185         PARA_INFO_LOG("successfully opened %s\n", info->short_name);
186         wn->private_data = pawd;
187         return 1;
188 fail:
189         free(pawd);
190         return ret;
191 }
192
193 __noreturn static void *aow_play(void *priv)
194 {
195         struct writer_node *wn = priv;
196         struct private_aow_data *pawd = wn->private_data;
197         struct btr_node *btrn = pawd->thread_btrn;
198         size_t frames, bytes;
199         char *data;
200         int ret;
201
202         for (;;) {
203                 /*
204                  * Lock mutex and wait for signal. pthread_cond_wait() will
205                  * automatically and atomically unlock mutex while it waits.
206                  */
207                 pthread_mutex_lock(&pawd->mutex);
208                 for (;;) {
209                         ret = btr_node_status(btrn, wn->min_iqs, BTR_NT_LEAF);
210                         if (ret < 0)
211                                 goto unlock;
212                         if (ret > 0) {
213                                 btr_merge(btrn, wn->min_iqs);
214                                 bytes = btr_next_buffer(btrn, &data);
215                                 frames = bytes / pawd->bytes_per_frame;
216                                 if (frames > 0)
217                                         break;
218                                 /* eof and less than a single frame available */
219                                 ret = -E_WRITE_COMMON_EOF;
220                                 goto unlock;
221                         }
222                         //PARA_CRIT_LOG("waiting for data\n");
223                         //usleep(1000);
224                         //pthread_mutex_unlock(&pawd->mutex);
225                         pthread_cond_wait(&pawd->data_available, &pawd->mutex);
226                 }
227                 pthread_mutex_unlock(&pawd->mutex);
228                 assert(frames > 0);
229                 bytes = frames * pawd->bytes_per_frame;
230                 ret = -E_AO_PLAY;
231                 if (ao_play(pawd->dev, data, bytes) == 0) /* failure */
232                         goto out;
233                 pthread_mutex_lock(&pawd->mutex);
234                 btr_consume(btrn, bytes);
235                 pthread_mutex_unlock(&pawd->mutex);
236         }
237 unlock:
238         pthread_mutex_unlock(&pawd->mutex);
239 out:
240         assert(ret < 0);
241         PARA_NOTICE_LOG("%s\n", para_strerror(-ret));
242         pthread_exit(NULL);
243 }
244
245 static int aow_create_thread(struct writer_node *wn)
246 {
247         struct private_aow_data *pawd = wn->private_data;
248         int ret;
249         const char *msg;
250
251         /* initialize with default attributes */
252         msg = "could not init mutex";
253         ret = pthread_mutex_init(&pawd->mutex, NULL);
254         if (ret < 0)
255                 goto fail;
256
257         msg = "could not initialize condition variable";
258         ret = pthread_cond_init(&pawd->data_available, NULL);
259         if (ret < 0)
260                 goto fail;
261
262         msg = "could not initialize thread attributes";
263         ret = pthread_attr_init(&pawd->attr);
264         if (ret < 0)
265                 goto fail;
266
267         /* schedule this thread under the real-time policy SCHED_FIFO */
268         msg = "could not set sched policy";
269         ret = pthread_attr_setschedpolicy(&pawd->attr, SCHED_FIFO);
270         if (ret < 0)
271                 goto fail;
272
273         msg = "could not set detach state to joinable";
274         ret = pthread_attr_setdetachstate(&pawd->attr, PTHREAD_CREATE_JOINABLE);
275         if (ret < 0)
276                 goto fail;
277
278         msg = "could not create thread";
279         ret = pthread_create(&pawd->thread, &pawd->attr, aow_play, wn);
280         if (ret < 0)
281                 goto fail;
282         return 1;
283 fail:
284         PARA_ERROR_LOG("%s (%s)\n", msg, strerror(ret));
285         return -E_AO_PTHREAD;
286 }
287
288 static int aow_post_select(__a_unused struct sched *s,
289                 struct task *t)
290 {
291         struct writer_node *wn = container_of(t, struct writer_node, task);
292         struct private_aow_data *pawd = wn->private_data;
293         int ret;
294
295         if (!pawd) {
296                 int32_t rate, ch, format;
297                 struct btr_node_description bnd;
298
299                 ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
300                 if (ret < 0)
301                         goto remove_btrn;
302                 if (ret == 0)
303                         return 0;
304                 get_btr_sample_rate(wn->btrn, &rate);
305                 get_btr_channels(wn->btrn, &ch);
306                 get_btr_sample_format(wn->btrn, &format);
307                 ret = aow_init(wn, rate, ch, format);
308                 if (ret < 0)
309                         goto remove_btrn;
310                 pawd = wn->private_data;
311
312                 /* set up thread btr node */
313                 bnd.name = "ao_thread_btrn";
314                 bnd.parent = wn->btrn;
315                 bnd.child = NULL;
316                 bnd.handler = NULL;
317                 bnd.context = pawd;
318                 pawd->thread_btrn = btr_new_node(&bnd);
319                 wn->private_data = pawd;
320
321                 ret = aow_create_thread(wn);
322                 if (ret < 0)
323                         goto remove_thread_btrn;
324                 return 0;
325         }
326         pthread_mutex_lock(&pawd->mutex);
327         ret = btr_node_status(wn->btrn, wn->min_iqs, BTR_NT_LEAF);
328         if (ret > 0) {
329                 btr_pushdown(wn->btrn);
330                 pthread_cond_signal(&pawd->data_available);
331         }
332         pthread_mutex_unlock(&pawd->mutex);
333         if (ret >= 0)
334                 goto out;
335         pthread_mutex_lock(&pawd->mutex);
336         btr_remove_node(&wn->btrn);
337         PARA_INFO_LOG("waiting for thread to terminate\n");
338         pthread_cond_signal(&pawd->data_available);
339         pthread_mutex_unlock(&pawd->mutex);
340         pthread_join(pawd->thread, NULL);
341 remove_thread_btrn:
342         btr_remove_node(&pawd->thread_btrn);
343 remove_btrn:
344         btr_remove_node(&wn->btrn);
345 out:
346         return ret;
347 }
348
349 __malloc static void *aow_parse_config_or_die(int argc, char **argv)
350 {
351         struct ao_write_args_info *conf = para_calloc(sizeof(*conf));
352
353         /* exits on errors */
354         ao_write_cmdline_parser(argc, argv, conf);
355         return conf;
356 }
357
358 static void aow_free_config(void *conf)
359 {
360         ao_write_cmdline_parser_free(conf);
361 }
362
363 /**
364  * The init function of the ao writer.
365  *
366  * \param w Pointer to the writer to initialize.
367  *
368  * \sa struct writer.
369  */
370 void ao_write_init(struct writer *w)
371 {
372         struct ao_write_args_info dummy;
373         int i, j, num_drivers, num_lines;
374         ao_info **driver_list;
375         char **dh; /* detailed help */
376
377         ao_write_cmdline_parser_init(&dummy);
378         w->close = aow_close;
379         w->pre_select = aow_pre_select;
380         w->post_select = aow_post_select;
381         w->parse_config_or_die = aow_parse_config_or_die;
382         w->free_config = aow_free_config;
383         w->help = (struct ggo_help)DEFINE_GGO_HELP(ao_write);
384         /* create detailed help containing all supported drivers/options */
385         for (i = 0; ao_write_args_info_detailed_help[i]; i++)
386                 ; /* nothing */
387         num_lines = i;
388         dh = para_malloc((num_lines + 3) * sizeof(char *));
389         for (i = 0; i < num_lines; i++)
390                 dh[i] = para_strdup(ao_write_args_info_detailed_help[i]);
391         dh[num_lines++] = para_strdup("libao drivers available on this host:");
392         dh[num_lines++] = para_strdup("~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~");
393
394         ao_initialize();
395         driver_list = ao_driver_info_list(&num_drivers);
396
397         for (i = 0; i < num_drivers; i++) {
398                 ao_info *info = driver_list[i];
399                 char *keys = NULL, *tmp = NULL;
400
401                 if (info->type == AO_TYPE_FILE)
402                         continue;
403                 for (j = 0; j < info->option_count; j++) {
404                         tmp = make_message("%s%s%s", keys? keys : "",
405                                 keys? ", " : "",
406                                 info->options[j]);
407                         free(keys);
408                         keys = tmp;
409                 }
410                 dh = para_realloc(dh, (num_lines + 6) * sizeof(char *));
411                 dh[num_lines++] = make_message("%s: %s", info->short_name, info->name);
412                 dh[num_lines++] = make_message("priority: %d", info->priority);
413                 dh[num_lines++] = make_message("keys: %s", keys? keys : "[none]");
414                 dh[num_lines++] = make_message("comment: %s", info->comment?
415                         info->comment : "[none]");
416                 dh[num_lines++] = para_strdup(NULL);
417                 free(keys);
418         }
419         dh[num_lines] = NULL;
420         w->help.detailed_help = (const char **)dh;
421         ao_write_cmdline_parser_free(&dummy);
422         ao_shutdown();
423 }
424