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