libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

flac_extractor.c (14126B)


      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
     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
     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
    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
    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
    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
    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 *
    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
    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
    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
    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
    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 */