"Fossies" - the Fresh Open Source Software Archive

Member "libextractor-1.11/src/plugins/flac_extractor.c" (30 Jan 2021, 14126 Bytes) of package /linux/privat/libextractor-1.11.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "flac_extractor.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 1.5_vs_1.6.

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