testsuite: Introduce a new test for para_server.
[paraslash.git] / oggdec_filter.c
1 /*
2 * Copyright (C) 2005-2011 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 "list.h"
15 #include "sched.h"
16 #include "ggo.h"
17 #include "buffer_tree.h"
18 #include "filter.h"
19 #include "error.h"
20 #include "string.h"
21
22 /** Determine byte sex. */
23 #ifdef WORDS_BIGENDIAN
24 #define ENDIAN 1
25 #else
26 #define ENDIAN 0
27 #endif
28
29 /** Data specific to the oggdec filter. */
30 struct private_oggdec_data {
31 /** Describes an ogg vorbis file. */
32 OggVorbis_File *vf;
33 /** The number of bytes consumed from the input buffer. */
34 size_t converted;
35 /** The number of channels of the current stream. */
36 unsigned int channels;
37 /** Current sample rate in Hz. */
38 unsigned int sample_rate;
39 /** Whether everything was decoded during the previous iteration. */
40 bool have_more;
41 };
42
43 static size_t cb_read(void *buf, size_t size, size_t nmemb, void *datasource)
44 {
45 struct filter_node *fn = datasource;
46 struct private_oggdec_data *pod = fn->private_data;
47 struct btr_node *btrn = fn->btrn;
48 char *btr_buf;
49 size_t nbytes = btr_next_buffer(btrn, &btr_buf), tmp;
50
51 /**
52 * oggvorbis always uses size == 1. Other sizes would complicate the code
53 * for no real gain. So we simply don't support size != 1.
54 */
55 assert(size == 1);
56 assert(pod->converted <= nbytes);
57 tmp = nbytes - pod->converted;
58 PARA_DEBUG_LOG("vorbis requests %zu bytes have %zu\n", nmemb, tmp);
59 tmp = PARA_MIN(tmp, nmemb);
60 if (tmp == 0)
61 return 0;
62 memcpy(buf, btr_buf + pod->converted, tmp);
63 pod->converted += tmp;
64 return tmp;
65 }
66
67 /*
68 * Custom data seeking function.
69 *
70 * Since we want the data source to be treated as unseekable at all
71 * times, the provided seek callback always returns -1 (failure).
72 */
73 static int cb_seek(__a_unused void *datasource, __a_unused ogg_int64_t offset,
74 __a_unused int whence)
75 {
76 return -1;
77 }
78
79 static int cb_close(__a_unused void *datasource)
80 {
81 return 0;
82 }
83
84 static const ov_callbacks ovc = {
85 .read_func = cb_read,
86 .seek_func = cb_seek,
87 .close_func = cb_close,
88 /*
89 * The tell function need not be provided if the data IO abstraction is
90 * not seekable
91 */
92 .tell_func = NULL
93 };
94
95 static void ogg_open(struct filter_node *fn)
96 {
97 fn->private_data = para_calloc(sizeof(struct private_oggdec_data));
98 fn->min_iqs = 8000;
99 }
100
101 static void ogg_close(struct filter_node *fn)
102 {
103 struct private_oggdec_data *pod = fn->private_data;
104
105 if (pod && pod->vf) {
106 PARA_DEBUG_LOG("ov_clearing %p, pod = %p\n", pod->vf, pod);
107 ov_clear(pod->vf);
108 free(pod->vf);
109 pod->vf = NULL;
110 } else
111 PARA_DEBUG_LOG("nothing to close\n");
112 free(pod);
113 fn->private_data = NULL;
114 }
115
116 static int oggdec_execute(struct btr_node *btrn, const char *cmd, char **result)
117 {
118 struct filter_node *fn = btr_context(btrn);
119 struct private_oggdec_data *pod = fn->private_data;
120
121 return decoder_execute(cmd, pod->sample_rate, pod->channels, result);
122 }
123
124 static int ogg_init(struct filter_node *fn)
125 {
126 struct private_oggdec_data *pod = fn->private_data;
127 struct btr_node *btrn = fn->btrn;
128 int ret, oret;
129 size_t iqs;
130 struct OggVorbis_File *vf = para_malloc(sizeof(*vf));
131
132 PARA_NOTICE_LOG("iqs: %zu, min_iqs: %zu, opening ov callbacks\n",
133 btr_get_input_queue_size(btrn), fn->min_iqs);
134 open:
135 oret = ov_open_callbacks(fn, vf,
136 NULL, /* no initial buffer */
137 0, /* no initial bytes */
138 ovc); /* the ov_open_callbacks */
139 if (oret == OV_ENOTVORBIS || oret == OV_EBADHEADER) {
140 /* this might be due to the input buffer being too small */
141 if (!btr_no_parent(btrn)) {
142 fn->min_iqs += 1000;
143 iqs = btr_get_input_queue_size(btrn);
144 ret = 0;
145 if (iqs < fn->min_iqs)
146 goto out;
147 PARA_CRIT_LOG("iqs: %zu\n", iqs);
148 btr_merge(btrn, fn->min_iqs);
149 pod->converted = 0;
150 goto open;
151 }
152 ret = (oret == OV_ENOTVORBIS)?
153 -E_OGGDEC_NOTVORBIS : -E_OGGDEC_BADHEADER;
154 goto out;
155 }
156 ret = -E_OGGDEC_READ;
157 if (oret == OV_EREAD)
158 goto out;
159 ret = -E_OGGDEC_VERSION;
160 if (oret == OV_EVERSION)
161 goto out;
162 ret = -E_OGGDEC_FAULT;
163 if (oret < 0)
164 goto out;
165 pod->channels = ov_info(vf, 0)->channels;
166 pod->sample_rate = ov_info(vf, 0)->rate;
167 PARA_NOTICE_LOG("%d channels, %d Hz\n", pod->channels,
168 pod->sample_rate);
169 ret = 1;
170 out:
171 if (ret <= 0)
172 free(vf);
173 else {
174 btr_consume(btrn, pod->converted);
175 pod->converted = 0;
176 fn->min_iqs = 0;
177 pod->vf = vf;
178 pod->have_more = true;
179 }
180 return ret;
181 }
182
183 #define OGGDEC_MAX_OUTPUT_SIZE (96 * 1024)
184 #define OGGDEC_OUTPUT_CHUNK_SIZE (32 * 1024)
185
186 static void ogg_pre_select(struct sched *s, struct task *t)
187 {
188 struct filter_node *fn = container_of(t, struct filter_node, task);
189 struct private_oggdec_data *pod = fn->private_data;
190 struct btr_node *btrn = fn->btrn;
191 int ret;
192
193 ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
194 if (ret != 0)
195 return sched_min_delay(s);
196 if (!pod->have_more)
197 return;
198 if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
199 return;
200 sched_min_delay(s);
201 }
202
203 static void ogg_post_select(__a_unused struct sched *s, struct task *t)
204 {
205 struct filter_node *fn = container_of(t, struct filter_node, task);
206 struct private_oggdec_data *pod = fn->private_data;
207 struct btr_node *btrn = fn->btrn;
208 int ret, have;
209 char *buf;
210
211 ret = btr_node_status(btrn, fn->min_iqs, BTR_NT_INTERNAL);
212 if (ret < 0 && ret != -E_BTR_EOF) /* fatal error */
213 goto out;
214 if (ret <= 0 && !pod->have_more) /* nothing to do */
215 goto out;
216 if (!pod->vf) {
217 if (ret <= 0)
218 goto out;
219 btr_merge(btrn, fn->min_iqs);
220 ret = ogg_init(fn);
221 goto out;
222 }
223 have = 0;
224 buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
225 for (;;) {
226 ret = ov_read(pod->vf, buf + have, OGGDEC_OUTPUT_CHUNK_SIZE - have,
227 ENDIAN, 2 /* 16 bit */, 1 /* signed */, NULL);
228 btr_consume(btrn, pod->converted);
229 pod->converted = 0;
230 if (ret <= 0)
231 break;
232 fn->min_iqs = 0;
233 have += ret;
234 if (have < OGGDEC_OUTPUT_CHUNK_SIZE)
235 continue;
236 if (btr_get_output_queue_size(btrn) > OGGDEC_MAX_OUTPUT_SIZE)
237 break;
238 btr_add_output(buf, have, btrn);
239 buf = para_malloc(OGGDEC_OUTPUT_CHUNK_SIZE);
240 have = 0;
241 }
242 pod->have_more = (ret > 0);
243 if (have > 0)
244 btr_add_output(buf, have, btrn);
245 else
246 free(buf);
247 if (ret == OV_HOLE) /* avoid buffer underruns */
248 fn->min_iqs = 9000;
249 if (ret >= 0 || ret == OV_HOLE)
250 return;
251 ret = -E_OGGDEC_BADLINK;
252 out:
253 t->error = ret;
254 if (ret < 0)
255 btr_remove_node(btrn);
256 }
257
258 /**
259 * The init function of the ogg vorbis decoder.
260 *
261 * \param f Its fields are filled in by the function.
262 */
263 void oggdec_filter_init(struct filter *f)
264 {
265 f->open = ogg_open;
266 f->close = ogg_close;
267 f->pre_select = ogg_pre_select;
268 f->post_select = ogg_post_select;
269 f->execute = oggdec_execute;
270 }