aboutsummaryrefslogtreecommitdiff
path: root/src/plugins/flac_extractor.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/flac_extractor.c')
-rw-r--r--src/plugins/flac_extractor.c376
1 files changed, 376 insertions, 0 deletions
diff --git a/src/plugins/flac_extractor.c b/src/plugins/flac_extractor.c
new file mode 100644
index 0000000..b6c7957
--- /dev/null
+++ b/src/plugins/flac_extractor.c
@@ -0,0 +1,376 @@
1/*
2 This file is part of libextractor.
3 (C) 2007, 2009 Vidyut Samanta and Christian Grothoff
4
5 libextractor is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published
7 by the Free Software Foundation; either version 2, or (at your
8 option) any later version.
9
10 libextractor is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with libextractor; see the file COPYING. If not, write to the
17 Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA.
19 */
20
21#include "platform.h"
22#include "extractor.h"
23
24#define FLAC_HEADER "fLaC"
25
26#if HAVE_FLAC_ALL_H
27#include <FLAC/all.h>
28#else
29#error You must install the libflac header files!
30#endif
31
32
33struct Context {
34 const char * data;
35 size_t size;
36 size_t pos;
37 EXTRACTOR_MetaDataProcessor proc;
38 void *proc_cls;
39 int ret;
40};
41
42static FLAC__StreamDecoderReadStatus
43flac_read (const FLAC__StreamDecoder *decoder,
44 FLAC__byte buffer[],
45 size_t *bytes,
46 void *client_data)
47{
48 struct Context * ctx = client_data;
49
50 if (*bytes <= 0)
51 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
52 if (*bytes > ctx->size - ctx->pos)
53 *bytes = ctx->size - ctx->pos;
54 if (*bytes == 0)
55 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
56 memcpy(buffer,
57 &ctx->data[ctx->pos],
58 *bytes);
59 ctx->pos += *bytes;
60 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
61}
62
63static FLAC__StreamDecoderSeekStatus
64flac_seek(const FLAC__StreamDecoder *decoder,
65 FLAC__uint64 absolute_byte_offset, void *client_data)
66{
67 struct Context * ctx = client_data;
68
69 if (absolute_byte_offset > ctx->size)
70 return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
71 ctx->pos = (size_t) absolute_byte_offset;
72 return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
73}
74
75static FLAC__StreamDecoderTellStatus
76flac_tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data)
77{
78 struct Context * ctx = client_data;
79
80 *absolute_byte_offset = ctx->pos;
81 return FLAC__STREAM_DECODER_TELL_STATUS_OK;
82}
83
84static FLAC__StreamDecoderLengthStatus
85flac_length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data)
86{
87 struct Context * ctx = client_data;
88
89 ctx->pos = *stream_length;
90 return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
91}
92
93static FLAC__bool
94flac_eof(const FLAC__StreamDecoder *decoder, void *client_data)
95{
96 struct Context * ctx = client_data;
97
98 return (ctx->pos == ctx->size) ? true : false;
99}
100
101static FLAC__StreamDecoderWriteStatus
102flac_write(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data)
103{
104 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
105}
106
107typedef struct
108{
109 const char *text;
110 enum EXTRACTOR_MetaType type;
111} Matches;
112
113static Matches tmap[] = {
114 {"TITLE", EXTRACTOR_METATYPE_TITLE},
115 {"VERSION", EXTRACTOR_METATYPE_SONG_VERSION},
116 {"ALBUM", EXTRACTOR_METATYPE_ALBUM},
117 {"ARTIST", EXTRACTOR_METATYPE_ARTIST},
118 {"PERFORMER", EXTRACTOR_METATYPE_PERFORMER},
119 {"COPYRIGHT", EXTRACTOR_METATYPE_COPYRIGHT},
120 {"LICENSE", EXTRACTOR_METATYPE_LICENSE},
121 {"ORGANIZATION", EXTRACTOR_METATYPE_ORGANIZATION},
122 {"DESCRIPTION", EXTRACTOR_METATYPE_DESCRIPTION},
123 {"GENRE", EXTRACTOR_METATYPE_GENRE},
124 {"DATE", EXTRACTOR_METATYPE_CREATION_DATE},
125 {"LOCATION", EXTRACTOR_METATYPE_LOCATION_SUBLOCATION},
126 {"CONTACT", EXTRACTOR_METATYPE_CONTACT_INFORMATION},
127 {"TRACKNUMBER", EXTRACTOR_METATYPE_TRACK_NUMBER},
128 {"ISRC", EXTRACTOR_METATYPE_ISRC},
129 {NULL, 0},
130};
131
132
133static char * xstrndup(const char * s, size_t n){
134 char * d;
135
136 d= malloc(n+1);
137 memcpy(d,s,n);
138 d[n]='\0';
139 return d;
140}
141
142#define ADD(t,s) do { if (ctx->ret == 0) ctx->ret = ctx->proc (ctx->proc_cls, "flac", t, EXTRACTOR_METAFORMAT_UTF8, "text/plain", s, strlen(s)+1); } while (0)
143
144
145static void
146check(const char * type,
147 unsigned int type_length,
148 const char * value,
149 unsigned int value_length,
150 struct Context *ctx)
151{
152 unsigned int i;
153 char *tmp;
154
155 i = 0;
156 while (tmap[i].text != NULL)
157 {
158 if ( (type_length == strlen(tmap[i].text)) &&
159 (0 == strncasecmp(tmap[i].text,
160 type,
161 type_length)) )
162 {
163 tmp = xstrndup(value,
164 value_length);
165 ADD (tmap[i].type, tmp);
166 free (tmp);
167 break;
168 }
169 i++;
170 }
171}
172
173
174static void
175flac_metadata(const FLAC__StreamDecoder *decoder,
176 const FLAC__StreamMetadata *metadata,
177 void *client_data)
178{
179 struct Context * ctx = client_data;
180
181 switch (metadata->type)
182 {
183 case FLAC__METADATA_TYPE_STREAMINFO:
184 {
185 char buf[512];
186 snprintf(buf, 512,
187 _("%u Hz, %u channels"),
188 metadata->data.stream_info.sample_rate,
189 metadata->data.stream_info.channels);
190 ADD (EXTRACTOR_METATYPE_RESOURCE_TYPE, buf);
191 break;
192 }
193 case FLAC__METADATA_TYPE_APPLICATION:
194 /* FIXME: could find out generator application here:
195 http://flac.sourceforge.net/api/structFLAC____StreamMetadata__Application.html and
196 http://flac.sourceforge.net/id.html
197 */
198 break;
199 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
200 {
201 const FLAC__StreamMetadata_VorbisComment * vc = &metadata->data.vorbis_comment;
202 unsigned int count = vc->num_comments;
203 const FLAC__StreamMetadata_VorbisComment_Entry * entry;
204 const char * eq;
205 unsigned int len;
206 unsigned int ilen;
207
208 while (count-- > 0)
209 {
210 entry = &vc->comments[count];
211 eq = (const char*) entry->entry;
212 len = entry->length;
213 ilen = 0;
214 while ( ('=' != *eq) && (*eq != '\0') &&
215 (ilen < len) )
216 {
217 eq++;
218 ilen++;
219 }
220 if ( ('=' != *eq) ||
221 (ilen == len) )
222 break;
223 eq++;
224 check((const char*) entry->entry,
225 ilen,
226 eq,
227 len - ilen,
228 ctx);
229 }
230 break;
231 }
232 case FLAC__METADATA_TYPE_PICTURE:
233 {
234 if (ctx->ret == 0)
235 {
236 enum EXTRACTOR_MetaType type;
237 switch (metadata->data.picture.type)
238 {
239 case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER:
240 case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD:
241 case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON:
242 type = EXTRACTOR_METATYPE_THUMBNAIL;
243 break;
244 case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER:
245 case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER:
246 type = EXTRACTOR_METATYPE_COVER_PICTURE;
247 break;
248 case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST:
249 case FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST:
250 case FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR:
251 case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND:
252 case FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER:
253 case FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST:
254 type = EXTRACTOR_METATYPE_CONTRIBUTOR_PICTURE;
255 break;
256 case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION:
257 case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING:
258 case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE:
259 case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE:
260 type = EXTRACTOR_METATYPE_EVENT_PICTURE;
261 break;
262 case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE:
263 case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE:
264 type = EXTRACTOR_METATYPE_LOGO;
265 break;
266 case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE:
267 case FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA:
268 case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH:
269 case FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION:
270 default:
271 type = EXTRACTOR_METATYPE_PICTURE;
272 break;
273 }
274 ctx->ret = ctx->proc (ctx->proc_cls,
275 "flac",
276 type,
277 EXTRACTOR_METAFORMAT_BINARY,
278 metadata->data.picture.mime_type,
279 (const char*) metadata->data.picture.data,
280 metadata->data.picture.data_length);
281 }
282 break;
283 }
284 case FLAC__METADATA_TYPE_PADDING:
285 case FLAC__METADATA_TYPE_SEEKTABLE:
286 case FLAC__METADATA_TYPE_CUESHEET:
287 case FLAC__METADATA_TYPE_UNDEFINED:
288 break;
289 }
290}
291
292static void
293flac_error(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data)
294{
295#if 0
296 fprintf(stderr,
297 "Got error: %u\n", status);
298#endif
299}
300
301/* mimetype = audio/flac */
302int
303EXTRACTOR_flac_extract (const char *data,
304 size_t size,
305 EXTRACTOR_MetaDataProcessor proc,
306 void *proc_cls,
307 const char *options)
308{
309 FLAC__StreamDecoder * decoder;
310 struct Context le_cls;
311 struct Context *ctx;
312
313 if (size < strlen(FLAC_HEADER) + sizeof (int))
314 return 0;
315 if (0 != memcmp(FLAC_HEADER,
316 data,
317 strlen(FLAC_HEADER)))
318 return 0;
319 decoder = FLAC__stream_decoder_new();
320 if (NULL == decoder)
321 return 0;
322 FLAC__stream_decoder_set_md5_checking(decoder, false);
323 FLAC__stream_decoder_set_metadata_ignore_all(decoder);
324 if (false == FLAC__stream_decoder_set_metadata_respond_all(decoder))
325 {
326 FLAC__stream_decoder_delete(decoder);
327 return 0;
328 }
329 le_cls.ret = 0;
330 le_cls.size = size;
331 le_cls.data = data;
332 le_cls.proc = proc;
333 le_cls.proc_cls = proc_cls;
334 le_cls.pos = 0;
335 if (FLAC__STREAM_DECODER_INIT_STATUS_OK !=
336 FLAC__stream_decoder_init_stream(decoder,
337 &flac_read,
338 &flac_seek,
339 &flac_tell,
340 &flac_length,
341 &flac_eof,
342 &flac_write,
343 &flac_metadata,
344 &flac_error,
345 &le_cls))
346 {
347 FLAC__stream_decoder_delete(decoder);
348 return le_cls.ret;
349 }
350 if (FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA)
351 {
352 FLAC__stream_decoder_delete(decoder);
353 return le_cls.ret;
354 }
355 if (! FLAC__stream_decoder_process_until_end_of_metadata(decoder))
356 {
357 FLAC__stream_decoder_delete(decoder);
358 return le_cls.ret;
359 }
360 switch (FLAC__stream_decoder_get_state(decoder))
361 {
362 case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
363 case FLAC__STREAM_DECODER_READ_METADATA:
364 case FLAC__STREAM_DECODER_END_OF_STREAM:
365 case FLAC__STREAM_DECODER_READ_FRAME:
366 ctx = &le_cls;
367 ADD (EXTRACTOR_METATYPE_MIMETYPE, "audio/flac");
368 break;
369 default:
370 /* not so sure... */
371 break;
372 }
373 FLAC__stream_decoder_finish (decoder);
374 FLAC__stream_decoder_delete(decoder);
375 return le_cls.ret;
376}