flac: Saner EOF handling
[cmus.git] / vorbis.c
blobdb49c6ce3d25451386a0bc425c6a367fa6adc928
1 /*
2 * Copyright 2004-2005 Timo Hirvonen
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License as
6 * published by the Free Software Foundation; either version 2 of the
7 * License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful, but
10 * WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
17 * 02111-1307, USA.
20 #include "ip.h"
21 #include "xmalloc.h"
22 #include "read_wrapper.h"
23 #include "debug.h"
24 #include "config/tremor.h"
26 #ifdef CONFIG_TREMOR
27 #include <tremor/ivorbisfile.h>
28 #else
29 #include <vorbis/vorbisfile.h>
30 #endif
32 #include <errno.h>
33 #include <string.h>
34 #include <sys/types.h>
35 #include <unistd.h>
37 struct vorbis_private {
38 OggVorbis_File vf;
39 int current_section;
42 /* http://www.xiph.org/vorbis/doc/vorbisfile/callbacks.html */
44 static size_t read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
46 struct input_plugin_data *ip_data = datasource;
47 int rc;
49 rc = read_wrapper(ip_data, ptr, size * nmemb);
50 if (rc == -1) {
51 d_print("error: %s\n", strerror(errno));
52 return 0;
54 if (rc == 0) {
55 errno = 0;
56 return 0;
58 return rc / size;
61 static int seek_func(void *datasource, ogg_int64_t offset, int whence)
63 struct input_plugin_data *ip_data = datasource;
64 int rc;
66 rc = lseek(ip_data->fd, offset, whence);
67 if (rc == -1)
68 return -1;
69 return 0;
72 static int close_func(void *datasource)
74 struct input_plugin_data *ip_data = datasource;
75 int rc;
77 rc = close(ip_data->fd);
78 ip_data->fd = -1;
79 return rc;
82 static long tell_func(void *datasource)
84 struct input_plugin_data *ip_data = datasource;
85 int rc;
87 rc = lseek(ip_data->fd, 0, SEEK_CUR);
88 return rc;
92 * typedef struct {
93 * size_t (*read_func) (void *ptr, size_t size, size_t nmemb, void *datasource);
94 * int (*seek_func) (void *datasource, ogg_int64_t offset, int whence);
95 * int (*close_func) (void *datasource);
96 * long (*tell_func) (void *datasource);
97 * } ov_callbacks;
99 static ov_callbacks callbacks = {
100 .read_func = read_func,
101 .seek_func = seek_func,
102 .close_func = close_func,
103 .tell_func = tell_func
106 static int vorbis_open(struct input_plugin_data *ip_data)
108 struct vorbis_private *priv;
109 vorbis_info *vi;
110 int rc;
112 priv = xnew(struct vorbis_private, 1);
113 priv->current_section = 0;
114 memset(&priv->vf, 0, sizeof(priv->vf));
116 rc = ov_open_callbacks(ip_data, &priv->vf, NULL, 0, callbacks);
117 if (rc != 0) {
118 d_print("ov_open failed: %d\n", rc);
119 free(priv);
120 return -IP_ERROR_FILE_FORMAT;
122 ip_data->private = priv;
124 vi = ov_info(&priv->vf, -1);
125 ip_data->sf = sf_rate(vi->rate) | sf_channels(vi->channels) | sf_bits(16) | sf_signed(1);
126 return 0;
129 static int vorbis_close(struct input_plugin_data *ip_data)
131 struct vorbis_private *priv;
132 int rc;
134 priv = ip_data->private;
135 /* this closes ip_data->fd! */
136 rc = ov_clear(&priv->vf);
137 ip_data->fd = -1;
138 if (rc)
139 d_print("ov_clear returned %d\n", rc);
140 free(priv);
141 ip_data->private = NULL;
142 return 0;
146 * OV_HOLE
147 * indicates there was an interruption in the data.
148 * (one of: garbage between pages, loss of sync followed by recapture,
149 * or a corrupt page)
150 * OV_EBADLINK
151 * indicates that an invalid stream section was supplied to libvorbisfile,
152 * or the requested link is corrupt.
154 * indicates EOF
156 * indicates actual number of bytes read. ov_read() will decode at most
157 * one vorbis packet per invocation, so the value returned will generally
158 * be less than length.
160 static int vorbis_read(struct input_plugin_data *ip_data, char *buffer, int count)
162 struct vorbis_private *priv;
163 int rc;
165 priv = ip_data->private;
166 #ifdef CONFIG_TREMOR
167 /* Tremor can only handle signed 16 bit data */
168 rc = ov_read(&priv->vf, buffer, count, &priv->current_section);
169 #else
170 rc = ov_read(&priv->vf, buffer, count, 0, 2, 1, &priv->current_section);
171 #endif
172 switch (rc) {
173 case OV_HOLE:
174 errno = EAGAIN;
175 return -1;
176 case OV_EBADLINK:
177 errno = EINVAL;
178 return -1;
179 case OV_EINVAL:
180 errno = EINVAL;
181 return -1;
182 case 0:
183 if (errno) {
184 d_print("error: %s\n", strerror(errno));
185 return -1;
186 /* return -IP_ERROR_INTERNAL; */
188 /* EOF */
189 return 0;
190 default:
191 if (rc < 0) {
192 d_print("error: %d\n", rc);
193 rc = -IP_ERROR_FILE_FORMAT;
195 return rc;
199 static int vorbis_seek(struct input_plugin_data *ip_data, double offset)
201 struct vorbis_private *priv;
202 int rc;
204 priv = ip_data->private;
206 #ifdef CONFIG_TREMOR
207 rc = ov_time_seek(&priv->vf, offset * 1000);
208 #else
209 rc = ov_time_seek(&priv->vf, offset);
210 #endif
211 switch (rc) {
212 case OV_ENOSEEK:
213 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
214 case OV_EINVAL:
215 return -IP_ERROR_INTERNAL;
216 case OV_EREAD:
217 return -IP_ERROR_INTERNAL;
218 case OV_EFAULT:
219 return -IP_ERROR_INTERNAL;
220 case OV_EBADLINK:
221 return -IP_ERROR_INTERNAL;
223 return 0;
226 static int vorbis_read_comments(struct input_plugin_data *ip_data,
227 struct keyval **comments)
229 GROWING_KEYVALS(c);
230 struct vorbis_private *priv;
231 vorbis_comment *vc;
232 int i;
234 priv = ip_data->private;
235 vc = ov_comment(&priv->vf, -1);
236 if (vc == NULL) {
237 d_print("vc == NULL\n");
238 *comments = xnew0(struct keyval, 1);
239 return 0;
241 for (i = 0; i < vc->comments; i++) {
242 const char *str = vc->user_comments[i];
243 const char *eq = strchr(str, '=');
244 char *key;
246 if (!eq) {
247 d_print("invalid comment: '%s' ('=' expected)\n", str);
248 continue;
251 key = xstrndup(str, eq - str);
252 comments_add_const(&c, key, eq + 1);
253 free(key);
255 keyvals_terminate(&c);
256 *comments = c.keyvals;
257 return 0;
260 static int vorbis_duration(struct input_plugin_data *ip_data)
262 struct vorbis_private *priv;
263 int duration;
265 priv = ip_data->private;
266 duration = ov_time_total(&priv->vf, -1);
267 if (duration == OV_EINVAL)
268 return -IP_ERROR_FUNCTION_NOT_SUPPORTED;
269 #ifdef CONFIG_TREMOR
270 duration = (duration + 500) / 1000;
271 #endif
272 return duration;
275 const struct input_plugin_ops ip_ops = {
276 .open = vorbis_open,
277 .close = vorbis_close,
278 .read = vorbis_read,
279 .seek = vorbis_seek,
280 .read_comments = vorbis_read_comments,
281 .duration = vorbis_duration
284 const char * const ip_extensions[] = { "ogg", NULL };
285 const char * const ip_mime_types[] = { "application/ogg", "audio/x-ogg", NULL };