1 /*
2 This file is part of libextractor.
3 Copyright (C) 2007, 2009, 2012 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 3, 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., 51 Franklin Street, Fifth Floor,
18 Boston, MA 02110-1301, USA.
19 */
20
21 /**
22 * @file plugins/flac_extractor.c
23 * @brief plugin to support FLAC files
24 * @author Christian Grothoff
25 */
26 #include "platform.h"
27 #include "extractor.h"
28 #include <FLAC/all.h>
29
30
31 /**
32 * Bytes each FLAC file must begin with (not used, but we might
33 * choose to add this back in the future to improve performance
34 * for non-ogg files).
35 */
36 #define FLAC_HEADER "fLaC"
37
38
39 /**
40 * Custom read function for flac.
41 *
42 * @param decoder unused
43 * @param buffer where to write the data
44 * @param bytes how many bytes to read, set to how many bytes were read
45 * @param client_data our 'struct EXTRACTOR_ExtractContxt*'
46 * @return status code (error, end-of-file or success)
47 */
48 static FLAC__StreamDecoderReadStatus
flac_read(const FLAC__StreamDecoder * decoder,FLAC__byte buffer[],size_t * bytes,void * client_data)49 flac_read (const FLAC__StreamDecoder *decoder,
50 FLAC__byte buffer[],
51 size_t *bytes,
52 void *client_data)
53 {
54 struct EXTRACTOR_ExtractContext *ec = client_data;
55 void *data;
56 ssize_t ret;
57
58 data = NULL;
59 ret = ec->read (ec->cls,
60 &data,
61 *bytes);
62 if (-1 == ret)
63 return FLAC__STREAM_DECODER_READ_STATUS_ABORT;
64 if (0 == ret)
65 {
66 errno = 0;
67 return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM;
68 }
69 memcpy (buffer, data, ret);
70 *bytes = ret;
71 errno = 0;
72 return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE;
73 }
74
75
76 /**
77 * Seek to a particular position in the file.
78 *
79 * @param decoder unused
80 * @param absolute_byte_offset where to seek
81 * @param client_data the 'struct EXTRACTOR_ExtractContext'
82 * @return status code (error or success)
83 */
84 static FLAC__StreamDecoderSeekStatus
flac_seek(const FLAC__StreamDecoder * decoder,FLAC__uint64 absolute_byte_offset,void * client_data)85 flac_seek (const FLAC__StreamDecoder *decoder,
86 FLAC__uint64 absolute_byte_offset,
87 void *client_data)
88 {
89 struct EXTRACTOR_ExtractContext *ec = client_data;
90
91 if (absolute_byte_offset !=
92 ec->seek (ec->cls, (int64_t) absolute_byte_offset, SEEK_SET))
93 return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR;
94 return FLAC__STREAM_DECODER_SEEK_STATUS_OK;
95 }
96
97
98 /**
99 * Tell FLAC about our current position in the file.
100 *
101 * @param decoder unused
102 * @param absolute_byte_offset location to store the current offset
103 * @param client_data the 'struct EXTRACTOR_ExtractContext'
104 * @return status code (error or success)
105 */
106 static FLAC__StreamDecoderTellStatus
flac_tell(const FLAC__StreamDecoder * decoder,FLAC__uint64 * absolute_byte_offset,void * client_data)107 flac_tell (const FLAC__StreamDecoder *decoder,
108 FLAC__uint64 *absolute_byte_offset,
109 void *client_data)
110 {
111 struct EXTRACTOR_ExtractContext *ec = client_data;
112
113 *absolute_byte_offset = ec->seek (ec->cls,
114 0,
115 SEEK_CUR);
116 return FLAC__STREAM_DECODER_TELL_STATUS_OK;
117 }
118
119
120 /**
121 * Tell FLAC the size of the file.
122 *
123 * @param decoder unused
124 * @param stream_length where to store the file size
125 * @param client_data the 'struct EXTRACTOR_ExtractContext'
126 * @return true at EOF, false if not
127 */
128 static FLAC__StreamDecoderLengthStatus
flac_length(const FLAC__StreamDecoder * decoder,FLAC__uint64 * stream_length,void * client_data)129 flac_length (const FLAC__StreamDecoder *decoder,
130 FLAC__uint64 *stream_length,
131 void *client_data)
132 {
133 struct EXTRACTOR_ExtractContext *ec = client_data;
134
135 *stream_length = ec->get_size (ec->cls);
136 return FLAC__STREAM_DECODER_LENGTH_STATUS_OK;
137 }
138
139
140 /**
141 * Tell FLAC if we are at the end of the file.
142 *
143 * @param decoder unused
144 * @param absolute_byte_offset location to store the current offset
145 * @param client_data the 'struct EXTRACTOR_ExtractContext'
146 * @return true at EOF, false if not
147 */
148 static FLAC__bool
flac_eof(const FLAC__StreamDecoder * decoder,void * client_data)149 flac_eof (const FLAC__StreamDecoder *decoder,
150 void *client_data)
151 {
152 struct EXTRACTOR_ExtractContext *ec = client_data;
153 uint64_t size;
154 int64_t seekresult;
155 size = ec->get_size (ec->cls);
156 seekresult = ec->seek (ec->cls, 0, SEEK_CUR);
157
158 if (seekresult == -1)
159 /* Treat seek error as error (not as indication of file not being
160 * seekable).
161 */
162 return true;
163 return (size == seekresult) ? true : false;
164 }
165
166
167 /**
168 * FLAC wants to write. Always succeeds but does nothing.
169 *
170 * @param decoder unused
171 * @param frame unused
172 * @param buffer unused
173 * @param client_data the 'struct EXTRACTOR_ExtractContext'
174 * @return always claims success
175 */
176 static FLAC__StreamDecoderWriteStatus
flac_write(const FLAC__StreamDecoder * decoder,const FLAC__Frame * frame,const FLAC__int32 * const buffer[],void * client_data)177 flac_write (const FLAC__StreamDecoder *decoder,
178 const FLAC__Frame *frame,
179 const FLAC__int32 *const buffer[],
180 void *client_data)
181 {
182 return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
183 }
184
185
186 /**
187 * A mapping from FLAC meta data strings to extractor types.
188 */
189 struct Matches
190 {
191 /**
192 * FLAC Meta data description text.
193 */
194 const char *text;
195
196 /**
197 * Corresponding LE type.
198 */
199 enum EXTRACTOR_MetaType type;
200 };
201
202
203 /**
204 * Mapping of FLAC meta data description texts to LE types.
205 * NULL-terminated.
206 */
207 static struct Matches tmap[] = {
208 {"TITLE", EXTRACTOR_METATYPE_TITLE},
209 {"VERSION", EXTRACTOR_METATYPE_SONG_VERSION},
210 {"ALBUM", EXTRACTOR_METATYPE_ALBUM},
211 {"ARTIST", EXTRACTOR_METATYPE_ARTIST},
212 {"PERFORMER", EXTRACTOR_METATYPE_PERFORMER},
213 {"COPYRIGHT", EXTRACTOR_METATYPE_COPYRIGHT},
214 {"LICENSE", EXTRACTOR_METATYPE_LICENSE},
215 {"ORGANIZATION", EXTRACTOR_METATYPE_ORGANIZATION},
216 {"DESCRIPTION", EXTRACTOR_METATYPE_DESCRIPTION},
217 {"GENRE", EXTRACTOR_METATYPE_GENRE},
218 {"DATE", EXTRACTOR_METATYPE_CREATION_DATE},
219 {"LOCATION", EXTRACTOR_METATYPE_LOCATION_SUBLOCATION},
220 {"CONTACT", EXTRACTOR_METATYPE_CONTACT_INFORMATION},
221 {"TRACKNUMBER", EXTRACTOR_METATYPE_TRACK_NUMBER},
222 {"ISRC", EXTRACTOR_METATYPE_ISRC},
223 {NULL, 0}
224 };
225
226
227 /**
228 * Give meta data to extractor.
229 *
230 * @param t type of the meta data
231 * @param s meta data value in utf8 format
232 */
233 #define ADD(t,s) do { ec->proc (ec->cls, "flac", t, EXTRACTOR_METAFORMAT_UTF8, \
234 "text/plain", s, strlen (s) + 1); } while (0)
235
236
237 /**
238 * Create 0-terminated version of n-character string.
239 *
240 * @param s input string (non 0-terminated)
241 * @param n number of bytes in 's'
242 * @return NULL on error, otherwise 0-terminated version of 's'
243 */
244 static char *
xstrndup(const char * s,size_t n)245 xstrndup (const char *s,
246 size_t n)
247 {
248 char *d;
249
250 if (NULL == (d = malloc (n + 1)))
251 return NULL;
252 memcpy (d, s, n);
253 d[n] = '\0';
254 return d;
255 }
256
257
258 /**
259 * Check if a mapping exists for the given meta data value
260 * and if so give the result to LE.
261 *
262 * @param type type of the meta data according to FLAC
263 * @param type_length number of bytes in 'type'
264 * @param value meta data as UTF8 string (non 0-terminated)
265 * @param value_length number of bytes in value
266 * @param ec extractor context
267 */
268 static void
check(const char * type,unsigned int type_length,const char * value,unsigned int value_length,struct EXTRACTOR_ExtractContext * ec)269 check (const char *type,
270 unsigned int type_length,
271 const char *value,
272 unsigned int value_length,
273 struct EXTRACTOR_ExtractContext *ec)
274 {
275 unsigned int i;
276 char *tmp;
277
278 for (i = 0; NULL != tmap[i].text; i++)
279 {
280 if ( (type_length != strlen (tmap[i].text)) ||
281 (0 != strncasecmp (tmap[i].text,
282 type,
283 type_length)) )
284 continue;
285 if (NULL ==
286 (tmp = xstrndup (value,
287 value_length)))
288 continue;
289 ADD (tmap[i].type, tmp);
290 free (tmp);
291 break;
292 }
293 }
294
295
296 /**
297 * Function called whenever FLAC finds meta data.
298 *
299 * @param decoder unused
300 * @param metadata meta data that was found
301 * @param client_data the 'struct EXTRACTOR_ExtractContext'
302 */
303 static void
flac_metadata(const FLAC__StreamDecoder * decoder,const FLAC__StreamMetadata * metadata,void * client_data)304 flac_metadata (const FLAC__StreamDecoder *decoder,
305 const FLAC__StreamMetadata *metadata,
306 void *client_data)
307 {
308 struct EXTRACTOR_ExtractContext *ec = client_data;
309 enum EXTRACTOR_MetaType type;
310 const FLAC__StreamMetadata_VorbisComment *vc;
311 unsigned int count;
312 const FLAC__StreamMetadata_VorbisComment_Entry *entry;
313 const char *eq;
314 unsigned int len;
315 unsigned int ilen;
316 char buf[128];
317
318 switch (metadata->type)
319 {
320 case FLAC__METADATA_TYPE_STREAMINFO:
321 {
322 snprintf (buf, sizeof (buf),
323 _ ("%u Hz, %u channels"),
324 metadata->data.stream_info.sample_rate,
325 metadata->data.stream_info.channels);
326 ADD (EXTRACTOR_METATYPE_RESOURCE_TYPE, buf);
327 break;
328 }
329 case FLAC__METADATA_TYPE_APPLICATION:
330 /* FIXME: could find out generator application here:
331 http://flac.sourceforge.net/api/structFLAC____StreamMetadata__Application.html and
332 http://flac.sourceforge.net/id.html
333 */
334 break;
335 case FLAC__METADATA_TYPE_VORBIS_COMMENT:
336 {
337 vc = &metadata->data.vorbis_comment;
338 count = vc->num_comments;
339 while (count-- > 0)
340 {
341 entry = &vc->comments[count];
342 eq = (const char*) entry->entry;
343 if (NULL == eq)
344 break;
345 len = entry->length;
346 ilen = 0;
347 while ( ('=' != *eq) && ('\0' != *eq) &&
348 (ilen < len) )
349 {
350 eq++;
351 ilen++;
352 }
353 if ( ('=' != *eq) ||
354 (ilen == len) )
355 break;
356 eq++;
357 check ((const char*) entry->entry,
358 ilen,
359 eq,
360 len - ilen,
361 ec);
362 }
363 break;
364 }
365 case FLAC__METADATA_TYPE_PICTURE:
366 {
367 switch (metadata->data.picture.type)
368 {
369 case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER:
370 case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD:
371 case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON:
372 type = EXTRACTOR_METATYPE_THUMBNAIL;
373 break;
374 case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER:
375 case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER:
376 type = EXTRACTOR_METATYPE_COVER_PICTURE;
377 break;
378 case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST:
379 case FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST:
380 case FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR:
381 case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND:
382 case FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER:
383 case FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST:
384 type = EXTRACTOR_METATYPE_CONTRIBUTOR_PICTURE;
385 break;
386 case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION:
387 case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING:
388 case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE:
389 case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE:
390 type = EXTRACTOR_METATYPE_EVENT_PICTURE;
391 break;
392 case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE:
393 case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE:
394 type = EXTRACTOR_METATYPE_LOGO;
395 break;
396 case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE:
397 case FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA:
398 case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH:
399 case FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION:
400 default:
401 type = EXTRACTOR_METATYPE_PICTURE;
402 break;
403 }
404 ec->proc (ec->cls,
405 "flac",
406 type,
407 EXTRACTOR_METAFORMAT_BINARY,
408 metadata->data.picture.mime_type,
409 (const char*) metadata->data.picture.data,
410 metadata->data.picture.data_length);
411 break;
412 }
413 default:
414 break;
415 }
416 }
417
418
419 /**
420 * Function called whenever FLAC decoder has trouble. Does nothing.
421 *
422 * @param decoder the decoder handle
423 * @param status type of the error
424 * @param client_data our 'struct EXTRACTOR_ExtractContext'
425 */
426 static void
flac_error(const FLAC__StreamDecoder * decoder,FLAC__StreamDecoderErrorStatus status,void * client_data)427 flac_error (const FLAC__StreamDecoder *decoder,
428 FLAC__StreamDecoderErrorStatus status,
429 void *client_data)
430 {
431 /* ignore errors */
432 }
433
434
435 /**
436 * Main entry method for the 'audio/flac' extraction plugin.
437 *
438 * @param ec extraction context provided to the plugin
439 */
440 void
EXTRACTOR_flac_extract_method(struct EXTRACTOR_ExtractContext * ec)441 EXTRACTOR_flac_extract_method (struct EXTRACTOR_ExtractContext *ec)
442 {
443 FLAC__StreamDecoder *decoder;
444
445 if (NULL == (decoder = FLAC__stream_decoder_new ()))
446 return;
447 FLAC__stream_decoder_set_md5_checking (decoder, false);
448 FLAC__stream_decoder_set_metadata_ignore_all (decoder);
449 if (false == FLAC__stream_decoder_set_metadata_respond_all (decoder))
450 {
451 FLAC__stream_decoder_delete (decoder);
452 return;
453 }
454 if (FLAC__STREAM_DECODER_INIT_STATUS_OK !=
455 FLAC__stream_decoder_init_stream (decoder,
456 &flac_read,
457 &flac_seek,
458 &flac_tell,
459 &flac_length,
460 &flac_eof,
461 &flac_write,
462 &flac_metadata,
463 &flac_error,
464 ec))
465 {
466 FLAC__stream_decoder_delete (decoder);
467 return;
468 }
469 if (FLAC__STREAM_DECODER_SEARCH_FOR_METADATA !=
470 FLAC__stream_decoder_get_state (decoder))
471 {
472 FLAC__stream_decoder_delete (decoder);
473 return;
474 }
475 if (! FLAC__stream_decoder_process_until_end_of_metadata (decoder))
476 {
477 FLAC__stream_decoder_delete (decoder);
478 return;
479 }
480 switch (FLAC__stream_decoder_get_state (decoder))
481 {
482 case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC:
483 case FLAC__STREAM_DECODER_READ_METADATA:
484 case FLAC__STREAM_DECODER_END_OF_STREAM:
485 case FLAC__STREAM_DECODER_READ_FRAME:
486 break;
487 default:
488 /* not so sure... */
489 break;
490 }
491 FLAC__stream_decoder_finish (decoder);
492 FLAC__stream_decoder_delete (decoder);
493 }
494
495
496 /* end of flac_extractor.c */
497