]> git.tuebingen.mpg.de Git - paraslash.git/blob - oggdec_filter.c
Consolidate decoder code by introducing prepare_filter_node().
[paraslash.git] / oggdec_filter.c
1 /*
2  * Copyright (C) 2005-2009 Andre Noll <maan@systemlinux.org>
3  *
4  * Licensed under the GPL v2. For licencing details see COPYING.
5  */
6
7 /** \file oggdec_filter.c Paraslash's ogg vorbis decoder. */
8
9 #include <regex.h>
10 #include <vorbis/vorbisfile.h>
11 #include <stdbool.h>
12
13 #include "para.h"
14 #include "oggdec_filter.cmdline.h"
15 #include "list.h"
16 #include "sched.h"
17 #include "ggo.h"
18 #include "buffer_tree.h"
19 #include "filter.h"
20 #include "error.h"
21 #include "string.h"
22
23 /** Determine byte sex. */
24 #ifdef WORDS_BIGENDIAN
25 #define ENDIAN 1
26 #else
27 #define ENDIAN 0
28 #endif
29
30 /** Data specific to the oggdec filter. */
31 struct private_oggdec_data {
32         /** Describes an ogg vorbis file. */
33         OggVorbis_File *vf;
34         /** The input buffer. */
35         char *inbuf;
36         /** The length of \a inbuf. */
37         size_t inbuf_len;
38         /** The number of bytes consumed from the input buffer. */
39         size_t converted;
40         /** When to start producing output. */
41         struct timeval stream_start;
42         /** The number of channels of the current stream. */
43         unsigned int channels;
44         /** Current sample rate in Hz. */
45         unsigned int samplerate;
46         size_t min_iqs;
47 };
48
49 static size_t cb_read_nobtr(void *buf, size_t size, size_t nmemb, void *datasource)
50 {
51         struct filter_node *fn = datasource;
52         struct private_oggdec_data *pod = fn->private_data;
53         size_t ret, have = pod->inbuf_len - pod->converted;
54         char *p = pod->inbuf + pod->converted;
55
56 //      PARA_DEBUG_LOG("pod = %p\n", pod);
57 //      PARA_DEBUG_LOG("vorbis requests %d bytes, have %d\n", size * nmemb, have);
58         if (pod->inbuf_len < size) {
59                 if (*fn->fc->input_error)
60                         return 0;
61                 errno = EAGAIN;
62                 return (size_t)-1;
63         }
64         ret = PARA_MIN(nmemb, have / size) * size;
65         memcpy(buf, p, ret);
66         pod->converted += ret;
67         return ret;
68 }
69
70 static size_t cb_read_btr(void *buf, size_t size, size_t nmemb, void *datasource)
71 {
72         struct filter_node *fn = datasource;
73         struct btr_node *btrn = fn->btrn;
74         size_t copied;
75
76         /**
77          * oggvorbis always uses size == 1. Other sizes would complicate the code
78          * for no real gain. So we simply don't support size != 1.
79          */
80         assert(size == 1);
81         //PARA_DEBUG_LOG("vorbis requests %zu x %zu = %zu bytes\n", size, nmemb, size * nmemb);
82         copied = 0;
83         for (;;) {
84                 char *btr_buf;
85                 size_t nbytes = btr_next_buffer(btrn, &btr_buf);
86                 if (nbytes == 0)
87                         break;
88                 nbytes = PARA_MIN(nmemb - copied, nbytes);
89                 memcpy(buf + copied, btr_buf, nbytes);
90                 copied += nbytes;
91                 btr_consume(btrn, nbytes);
92                 if (copied == nmemb)
93                         break;
94         }
95         return copied;
96 }
97
98 static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
99 {
100         struct filter_node *fn = datasource;
101
102         if (fn->btrn)
103                 return cb_read_btr(buf, size, nmemb, datasource);
104         else
105                 return cb_read_nobtr(buf, size, nmemb, datasource);
106 }
107
108 /*
109  * Custom data seeking function.
110  *
111  * Since we want the data source to be treated as unseekable at all
112  * times, the provided seek callback always returns -1 (failure).
113  */
114 static int cb_seek(__a_unused void *datasource, __a_unused ogg_int64_t offset,
115                 __a_unused int whence)
116 {
117         return -1;
118 }
119
120 static int cb_close(__a_unused void *datasource)
121 {
122         return 0;
123 }
124
125 static const ov_callbacks ovc = {
126         .read_func = cb_read,
127         .seek_func = cb_seek,
128         .close_func = cb_close,
129         /*
130          * The tell function need not be provided if the data IO abstraction is
131          * not seekable
132          */
133         .tell_func = NULL
134 };
135
136 static void ogg_open(struct filter_node *fn)
137 {
138         struct private_oggdec_data *pod = para_calloc(
139                 sizeof(struct private_oggdec_data));
140         struct oggdec_filter_args_info *conf = fn->conf;
141
142         fn->private_data = pod;
143         fn->bufsize = conf->bufsize_arg * 1024;
144         fn->buf = para_malloc(fn->bufsize);
145 }
146
147 static void ogg_close(struct filter_node *fn)
148 {
149         struct private_oggdec_data *pod = fn->private_data;
150         if (pod->vf) {
151                 PARA_DEBUG_LOG("ov_clearing %p, pod = %p\n", pod->vf, pod);
152                 ov_clear(pod->vf);
153                 free(pod->vf);
154                 pod->vf = NULL;
155         } else
156                 PARA_DEBUG_LOG("nothing to close in fc %p, pod = %p\n", pod->vf, pod);
157         free(fn->buf);
158         fn->buf = NULL;
159         free(fn->private_data);
160         fn->private_data = NULL;
161 }
162
163 #define OGGDEC_MAX_PENDING (640 * 1024)
164 #define OGGDEC_OUTPUT_CHUNK_SIZE (64 * 1024)
165
166
167 static int oggdec_execute(struct btr_node *btrn, const char *cmd, char **result)
168 {
169         struct filter_node *fn = btr_context(btrn);
170         struct private_oggdec_data *pod = fn->private_data;
171
172         if (!strcmp(cmd, "samplerate")) {
173                 if (pod->samplerate == 0)
174                         return -ERRNO_TO_PARA_ERROR(ENAVAIL);
175                 *result = make_message("%u", pod->samplerate);
176                 return 1;
177         }
178         if (!strcmp(cmd, "channels")) {
179                 if (pod->channels == 0)
180                         return -ERRNO_TO_PARA_ERROR(ENAVAIL);
181                 *result = make_message("%u", pod->channels);
182                 return 1;
183         }
184         return -ERRNO_TO_PARA_ERROR(ENOTSUP);
185 }
186
187 static void ogg_pre_select(struct sched *s, struct task *t)
188 {
189         struct filter_node *fn = container_of(t, struct filter_node, task);
190         size_t iqs = btr_get_input_queue_size(fn->btrn);
191
192         t->error = 0;
193         if (iqs == 0)
194                 return;
195         if (btr_bytes_pending(fn->btrn) > OGGDEC_MAX_PENDING)
196                 return; /* FIXME, should use reasonable bound on timeout */
197         s->timeout.tv_sec = 0;
198         s->timeout.tv_usec = 1;
199 }
200
201 static void ogg_post_select(__a_unused struct sched *s, struct task *t)
202 {
203         struct filter_node *fn = container_of(t, struct filter_node, task);
204         struct private_oggdec_data *pod = fn->private_data;
205         struct btr_node *btrn = fn->btrn;
206         size_t iqs, len;
207         int ret;
208         char *in;
209
210         t->error = 0;
211         ret = prepare_filter_node(btrn, pod->min_iqs);
212         if (ret < 0)
213                 goto err;
214         if (ret == 0)
215                 return;
216         len = btr_next_buffer(btrn, &in);
217         iqs = btr_get_input_queue_size(btrn);
218         if (!pod->vf) {
219                 int oret;
220
221                 pod->vf = para_malloc(sizeof(struct OggVorbis_File));
222                 PARA_NOTICE_LOG("input queue: %zu, opening ov callbacks\n", iqs);
223                 oret = ov_open_callbacks(fn, pod->vf,
224                         NULL, /* no initial buffer */
225                         0, /* no initial bytes */
226                         ovc); /* the ov_open_callbacks */
227                 if (oret == OV_ENOTVORBIS || oret == OV_EBADHEADER) {
228                         /* this might be due to the input buffer being too small */
229                         if (!btr_no_parent(btrn)) {
230                                 free(pod->vf);
231                                 pod->vf = NULL;
232                                 pod->min_iqs = iqs + 1;
233                                 return;
234                         }
235                         ret = (oret == OV_ENOTVORBIS)?
236                                 -E_OGGDEC_NOTVORBIS : -E_OGGDEC_BADHEADER;
237                         goto err;
238                 }
239                 ret = -E_OGGDEC_READ;
240                 if (oret == OV_EREAD)
241                         goto err;
242                 ret = -E_OGGDEC_VERSION;
243                 if (oret == OV_EVERSION)
244                         goto err;
245                 ret = -E_OGGDEC_FAULT;
246                 if (oret < 0)
247                         goto err;
248                 pod->channels = ov_info(pod->vf, 0)->channels;
249                 pod->samplerate = ov_info(pod->vf, 0)->rate;
250                 PARA_NOTICE_LOG("%d channels, %d Hz\n", pod->channels,
251                         pod->samplerate);
252                 ///* wait a bit to avoid buffer underruns */
253                 //tv_add(now, &(struct timeval){0, 500 * 1000}, &pod->stream_start);
254                 return;
255         }
256         for (;;) {
257                 char *out = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
258                 ssize_t read_ret = ov_read(pod->vf, out, OGGDEC_OUTPUT_CHUNK_SIZE,
259                         ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
260                 if (read_ret <= 0)
261                         free(out);
262                 if (read_ret == 0) {
263                         ret = -E_OGGDEC_EOF;
264                         if (btr_no_parent(btrn))
265                                 goto err;
266                         return;
267                 }
268                 if (read_ret == OV_HOLE)
269                         return;
270                 if (read_ret < 0) {
271                         ret = -E_OGGDEC_BADLINK;
272                         goto err;
273                 }
274                 btr_add_output(out, read_ret, btrn);
275         }
276
277 err:
278         assert(ret < 0);
279         ogg_close(fn);
280         t->error = ret;
281         btr_del_node(btrn);
282 }
283
284 static ssize_t ogg_convert(char *inbuffer, size_t len, struct filter_node *fn)
285 {
286         ssize_t ret;
287         struct private_oggdec_data *pod = fn->private_data;
288         struct oggdec_filter_args_info *conf = fn->conf;
289         /* make the buffer known to the read callback cb_read() */
290         pod->inbuf = inbuffer;
291         pod->inbuf_len = len;
292         pod->converted = 0;
293
294         if (!pod->vf) {
295                 if (*fn->fc->input_error < 0)
296                         return *fn->fc->input_error;
297                 if (!len)
298                         return 0;
299                 pod->vf = para_malloc(sizeof(struct OggVorbis_File));
300                 PARA_NOTICE_LOG("input buffer: %zd, opening ov callbacks\n", len);
301                 ret = ov_open_callbacks(fn, pod->vf,
302                         NULL, /* no initial buffer */
303                         0, /* no initial bytes */
304                         ovc); /* the ov_open_callbacks */
305                 if (ret == OV_ENOTVORBIS || ret == OV_EBADHEADER) {
306                         /* this might be due to the input buffer being too small */
307                         int ib = 1024 * conf->initial_buffer_arg; /* initial buffer */
308                         if (len < ib) {
309                                 PARA_INFO_LOG("initial input buffer %zd/%d, "
310                                         "waiting for more data\n", len, ib);
311                                 free(pod->vf);
312                                 pod->vf = NULL;
313                                 return 0;
314                         }
315                         return ret == OV_ENOTVORBIS?
316                                 -E_OGGDEC_NOTVORBIS : -E_OGGDEC_BADHEADER;
317                 }
318                 if (ret == OV_EREAD)
319                         return -E_OGGDEC_READ;
320                 if (ret == OV_EVERSION)
321                         return -E_OGGDEC_VERSION;
322                 if (ret < 0)
323                         return -E_OGGDEC_FAULT;
324                 fn->fc->channels = ov_info(pod->vf, 0)->channels;
325                 fn->fc->samplerate = ov_info(pod->vf, 0)->rate;
326                 PARA_NOTICE_LOG("%d channels, %d Hz\n", fn->fc->channels,
327                         fn->fc->samplerate);
328                 /* wait a bit to avoid buffer underruns */
329                 tv_add(now, &(struct timeval){0, 500 * 1000}, &pod->stream_start);
330                 return pod->converted;
331         }
332         if (tv_diff(now, &pod->stream_start, NULL) < 0) {
333                 PARA_DEBUG_LOG("initial delay..\n");
334                 return 0;
335         }
336         while (fn->loaded < fn->bufsize) {
337                 int length = fn->bufsize - fn->loaded;
338                 long read_ret = ov_read(pod->vf, fn->buf + fn->loaded, length,
339                         ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
340                 if (read_ret == 0)
341                         return pod->converted;
342                 if (read_ret == OV_HOLE) {
343                         if (!fn->loaded) {
344                                 PARA_INFO_LOG("hole, delaying playback\n");
345                                 tv_add(now, &(struct timeval){0, 500 * 1000}, &pod->stream_start);
346                         }
347                         return pod->converted;
348                 }
349                 if (read_ret < 0)
350                         return -E_OGGDEC_BADLINK;
351                 fn->loaded += read_ret;
352         }
353         return pod->converted;
354 }
355
356 static int oggdec_parse_config(int argc, char **argv, void **config)
357 {
358         int ret;
359         struct oggdec_filter_args_info *ogg_conf;
360
361         ogg_conf = para_calloc(sizeof(*ogg_conf));
362         ret = -E_OGGDEC_SYNTAX;
363         if (oggdec_cmdline_parser(argc, argv, ogg_conf))
364                 goto err;
365         ret = -ERRNO_TO_PARA_ERROR(EINVAL);
366         if (ogg_conf->bufsize_arg < 0)
367                 goto err;
368         if (ogg_conf->bufsize_arg >= INT_MAX / 1024)
369                 goto err;
370         if (ogg_conf->initial_buffer_arg < 0)
371                 goto err;
372         if (ogg_conf->initial_buffer_arg >= INT_MAX / 1024)
373                 goto err;
374         *config = ogg_conf;
375         return 1;
376 err:
377         free(ogg_conf);
378         return ret;
379 }
380
381 /**
382  * The init function of the ogg vorbis decoder.
383  *
384  * \param f Its fields are filled in by the function.
385  */
386 void oggdec_filter_init(struct filter *f)
387 {
388         struct oggdec_filter_args_info dummy;
389
390         oggdec_cmdline_parser_init(&dummy);
391         f->open = ogg_open;
392         f->close = ogg_close;
393         f->convert = ogg_convert;
394         f->pre_select = ogg_pre_select;
395         f->post_select = ogg_post_select;
396         f->parse_config = oggdec_parse_config;
397         f->execute = oggdec_execute;
398         f->help = (struct ggo_help) {
399                 .short_help = oggdec_filter_args_info_help,
400                 .detailed_help = oggdec_filter_args_info_detailed_help
401         };
402 }