libextractor

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

commit 6f15c520ee8390e07039a7089a5d83434cddfdd4
parent 2fbd84b9ecbc08f2a70944080c342184255b28f9
Author: Christian Grothoff <christian@grothoff.org>
Date:   Sun,  5 Aug 2012 20:46:38 +0000

first hack at refactoring FLAC to new API

Diffstat:
Msrc/plugins/flac_extractor.c | 498+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msrc/plugins/ogg_extractor.c | 4+++-
2 files changed, 301 insertions(+), 201 deletions(-)

diff --git a/src/plugins/flac_extractor.c b/src/plugins/flac_extractor.c @@ -1,10 +1,10 @@ /* This file is part of libextractor. - (C) 2007, 2009 Vidyut Samanta and Christian Grothoff + (C) 2007, 2009, 2012 Vidyut Samanta and Christian Grothoff libextractor is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published - by the Free Software Foundation; either version 2, or (at your + by the Free Software Foundation; either version 3, or (at your option) any later version. libextractor is distributed in the hope that it will be useful, but @@ -18,99 +18,185 @@ Boston, MA 02111-1307, USA. */ +/** + * @file plugins/flac_extractor.c + * @brief plugin to support FLAC files + * @author Christian Grothoff + */ #include "platform.h" #include "extractor.h" +#include <FLAC/all.h> + +/** + * Bytes each FLAC file must begin with (not used, but we might + * choose to add this back in the future to improve performance + * for non-ogg files). + */ #define FLAC_HEADER "fLaC" -#if HAVE_FLAC_ALL_H -#include <FLAC/all.h> -#else -#error You must install the libflac header files! -#endif - - -struct Context { - const char * data; - size_t size; - size_t pos; - EXTRACTOR_MetaDataProcessor proc; - void *proc_cls; - int ret; -}; +/** + * Custom read function for flac. + * + * @param decoder unused + * @param buffer where to write the data + * @param bytes how many bytes to read, set to how many bytes were read + * @param client_data our 'struct EXTRACTOR_ExtractContxt*' + * @return status code (error, end-of-file or success) + */ static FLAC__StreamDecoderReadStatus flac_read (const FLAC__StreamDecoder *decoder, FLAC__byte buffer[], size_t *bytes, void *client_data) { - struct Context * ctx = client_data; - - if (*bytes <= 0) + struct EXTRACTOR_ExtractContext *ec = client_data; + void *data; + ssize_t ret; + + data = NULL; + ret = ec->read (ec->cls, + &data, + *bytes); + if (-1 == ret) return FLAC__STREAM_DECODER_READ_STATUS_ABORT; - if (*bytes > ctx->size - ctx->pos) - *bytes = ctx->size - ctx->pos; - if (*bytes == 0) - return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; - memcpy(buffer, - &ctx->data[ctx->pos], - *bytes); - ctx->pos += *bytes; + if (0 == ret) + { + errno = 0; + return FLAC__STREAM_DECODER_READ_STATUS_END_OF_STREAM; + } + memcpy (buffer, data, ret); + *bytes = ret; + errno = 0; return FLAC__STREAM_DECODER_READ_STATUS_CONTINUE; } + +/** + * Seek to a particular position in the file. + * + * @param decoder unused + * @param absolute_byte_offset where to seek + * @param client_data the 'struct EXTRACTOR_ExtractContext' + * @return status code (error or success) + */ static FLAC__StreamDecoderSeekStatus -flac_seek(const FLAC__StreamDecoder *decoder, - FLAC__uint64 absolute_byte_offset, void *client_data) +flac_seek (const FLAC__StreamDecoder *decoder, + FLAC__uint64 absolute_byte_offset, + void *client_data) { - struct Context * ctx = client_data; - - if (absolute_byte_offset > ctx->size) + struct EXTRACTOR_ExtractContext *ec = client_data; + + if (absolute_byte_offset != + ec->seek (ec->cls, (int64_t) absolute_byte_offset, SEEK_SET)) return FLAC__STREAM_DECODER_SEEK_STATUS_ERROR; - ctx->pos = (size_t) absolute_byte_offset; return FLAC__STREAM_DECODER_SEEK_STATUS_OK; } + +/** + * Tell FLAC about our current position in the file. + * + * @param decoder unused + * @param absolute_byte_offset location to store the current offset + * @param client_data the 'struct EXTRACTOR_ExtractContext' + * @return status code (error or success) + */ static FLAC__StreamDecoderTellStatus -flac_tell(const FLAC__StreamDecoder *decoder, FLAC__uint64 *absolute_byte_offset, void *client_data) +flac_tell (const FLAC__StreamDecoder *decoder, + FLAC__uint64 *absolute_byte_offset, + void *client_data) { - struct Context * ctx = client_data; - - *absolute_byte_offset = ctx->pos; + struct EXTRACTOR_ExtractContext *ec = client_data; + + *absolute_byte_offset = ec->seek (ec->cls, + 0, + SEEK_CUR); return FLAC__STREAM_DECODER_TELL_STATUS_OK; } + +/** + * Tell FLAC the size of the file. + * + * @param decoder unused + * @param stream_length where to store the file size + * @param client_data the 'struct EXTRACTOR_ExtractContext' + * @return true at EOF, false if not + */ static FLAC__StreamDecoderLengthStatus -flac_length(const FLAC__StreamDecoder *decoder, FLAC__uint64 *stream_length, void *client_data) +flac_length (const FLAC__StreamDecoder *decoder, + FLAC__uint64 *stream_length, + void *client_data) { - struct Context * ctx = client_data; + struct EXTRACTOR_ExtractContext *ec = client_data; - ctx->pos = *stream_length; + *stream_length = ec->get_size (ec->cls); return FLAC__STREAM_DECODER_LENGTH_STATUS_OK; } + +/** + * Tell FLAC if we are at the end of the file. + * + * @param decoder unused + * @param absolute_byte_offset location to store the current offset + * @param client_data the 'struct EXTRACTOR_ExtractContext' + * @return true at EOF, false if not + */ static FLAC__bool -flac_eof(const FLAC__StreamDecoder *decoder, void *client_data) +flac_eof (const FLAC__StreamDecoder *decoder, + void *client_data) { - struct Context * ctx = client_data; + struct EXTRACTOR_ExtractContext *ec = client_data; - return (ctx->pos == ctx->size) ? true : false; + return (ec->get_size (ec->cls) == + ec->seek (ec->cls, 0, SEEK_CUR)) ? true : false; } + +/** + * FLAC wants to write. Always succeeds but does nothing. + * + * @param decoder unused + * @param frame unused + * @param buffer unused + * @param client_data the 'struct EXTRACTOR_ExtractContext' + * @return always claims success + */ static FLAC__StreamDecoderWriteStatus -flac_write(const FLAC__StreamDecoder *decoder, const FLAC__Frame *frame, const FLAC__int32 *const buffer[], void *client_data) +flac_write (const FLAC__StreamDecoder *decoder, + const FLAC__Frame *frame, + const FLAC__int32 *const buffer[], + void *client_data) { return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE; } -typedef struct + +/** + * A mapping from FLAC meta data strings to extractor types. + */ +struct Matches { + /** + * FLAC Meta data description text. + */ const char *text; + + /** + * Corresponding LE type. + */ enum EXTRACTOR_MetaType type; -} Matches; +}; -static Matches tmap[] = { + +/** + * Mapping of FLAC meta data description texts to LE types. + * NULL-terminated. + */ +static struct Matches tmap[] = { {"TITLE", EXTRACTOR_METATYPE_TITLE}, {"VERSION", EXTRACTOR_METATYPE_SONG_VERSION}, {"ALBUM", EXTRACTOR_METATYPE_ALBUM}, @@ -126,72 +212,97 @@ static Matches tmap[] = { {"CONTACT", EXTRACTOR_METATYPE_CONTACT_INFORMATION}, {"TRACKNUMBER", EXTRACTOR_METATYPE_TRACK_NUMBER}, {"ISRC", EXTRACTOR_METATYPE_ISRC}, - {NULL, 0}, + {NULL, 0} }; -static char * xstrndup(const char * s, size_t n){ +/** + * FIXME. + */ +#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) + + +/** + * Create 0-terminated version of n-character string. + * + * @param s input string (non 0-terminated) + * @param n number of bytes in 's' + * @return NULL on error, otherwise 0-terminated version of 's' + */ +static char * +xstrndup (const char *s, + size_t n) +{ char * d; - d= malloc(n+1); - if (d == NULL) + if (NULL == (d = malloc(n+1))) return NULL; - memcpy(d,s,n); - d[n]='\0'; + memcpy (d, s, n); + d[n] = '\0'; return d; } -#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) - +/** + * FIXME. + */ static void -check(const char * type, - unsigned int type_length, - const char * value, - unsigned int value_length, - struct Context *ctx) +check (const char *type, + unsigned int type_length, + const char *value, + unsigned int value_length, + struct EXTRACTOR_ExtractContext *ec) { unsigned int i; char *tmp; - i = 0; - while (tmap[i].text != NULL) + for (i=0; NULL != tmap[i].text; i++) { - if ( (type_length == strlen(tmap[i].text)) && - (0 == strncasecmp(tmap[i].text, - type, - type_length)) ) - { - tmp = xstrndup(value, - value_length); - if (tmp != NULL) - { - ADD (tmap[i].type, tmp); - free (tmp); - } - break; - } - i++; + if ( (type_length != strlen (tmap[i].text)) || + (0 != strncasecmp (tmap[i].text, + type, + type_length)) ) + continue; + if (NULL == + (tmp = xstrndup (value, + value_length))) + continue; + ADD (tmap[i].type, tmp); + free (tmp); } } +/** + * Function called whenever FLAC finds meta data. + * + * @param decoder unused + * @param metadata meta data that was found + * @param client_data the 'struct EXTRACTOR_ExtractContext' + */ static void -flac_metadata(const FLAC__StreamDecoder *decoder, - const FLAC__StreamMetadata *metadata, - void *client_data) +flac_metadata (const FLAC__StreamDecoder *decoder, + const FLAC__StreamMetadata *metadata, + void *client_data) { - struct Context * ctx = client_data; + struct EXTRACTOR_ExtractContext *ec = client_data; + enum EXTRACTOR_MetaType type; + const FLAC__StreamMetadata_VorbisComment * vc; + unsigned int count; + const FLAC__StreamMetadata_VorbisComment_Entry * entry; + const char * eq; + unsigned int len; + unsigned int ilen; switch (metadata->type) { case FLAC__METADATA_TYPE_STREAMINFO: { char buf[512]; - snprintf(buf, 512, - _("%u Hz, %u channels"), - metadata->data.stream_info.sample_rate, - metadata->data.stream_info.channels); + snprintf (buf, sizeof (buf), + _("%u Hz, %u channels"), + metadata->data.stream_info.sample_rate, + metadata->data.stream_info.channels); ADD (EXTRACTOR_METATYPE_RESOURCE_TYPE, buf); break; } @@ -203,13 +314,8 @@ flac_metadata(const FLAC__StreamDecoder *decoder, break; case FLAC__METADATA_TYPE_VORBIS_COMMENT: { - const FLAC__StreamMetadata_VorbisComment * vc = &metadata->data.vorbis_comment; - unsigned int count = vc->num_comments; - const FLAC__StreamMetadata_VorbisComment_Entry * entry; - const char * eq; - unsigned int len; - unsigned int ilen; - + vc = &metadata->data.vorbis_comment; + count = vc->num_comments; while (count-- > 0) { entry = &vc->comments[count]; @@ -226,64 +332,62 @@ flac_metadata(const FLAC__StreamDecoder *decoder, (ilen == len) ) break; eq++; - check((const char*) entry->entry, - ilen, - eq, - len - ilen, - ctx); + check ((const char*) entry->entry, + ilen, + eq, + len - ilen, + ctx); } break; } case FLAC__METADATA_TYPE_PICTURE: { - if (ctx->ret == 0) + if (0 != ctx->ret) + break; + switch (metadata->data.picture.type) { - enum EXTRACTOR_MetaType type; - switch (metadata->data.picture.type) - { - case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER: - case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD: - case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON: - type = EXTRACTOR_METATYPE_THUMBNAIL; - break; - case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER: - case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER: - type = EXTRACTOR_METATYPE_COVER_PICTURE; - break; - case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST: - case FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST: - case FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR: - case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND: - case FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER: - case FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST: - type = EXTRACTOR_METATYPE_CONTRIBUTOR_PICTURE; - break; - case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION: - case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING: - case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE: - case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE: - type = EXTRACTOR_METATYPE_EVENT_PICTURE; - break; - case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE: - case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE: - type = EXTRACTOR_METATYPE_LOGO; - break; - case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE: - case FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA: - case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH: - case FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION: - default: - type = EXTRACTOR_METATYPE_PICTURE; - break; - } - ctx->ret = ctx->proc (ctx->proc_cls, - "flac", - type, - EXTRACTOR_METAFORMAT_BINARY, - metadata->data.picture.mime_type, - (const char*) metadata->data.picture.data, - metadata->data.picture.data_length); + case FLAC__STREAM_METADATA_PICTURE_TYPE_OTHER: + case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON_STANDARD: + case FLAC__STREAM_METADATA_PICTURE_TYPE_FILE_ICON: + type = EXTRACTOR_METATYPE_THUMBNAIL; + break; + case FLAC__STREAM_METADATA_PICTURE_TYPE_FRONT_COVER: + case FLAC__STREAM_METADATA_PICTURE_TYPE_BACK_COVER: + type = EXTRACTOR_METATYPE_COVER_PICTURE; + break; + case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAD_ARTIST: + case FLAC__STREAM_METADATA_PICTURE_TYPE_ARTIST: + case FLAC__STREAM_METADATA_PICTURE_TYPE_CONDUCTOR: + case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND: + case FLAC__STREAM_METADATA_PICTURE_TYPE_COMPOSER: + case FLAC__STREAM_METADATA_PICTURE_TYPE_LYRICIST: + type = EXTRACTOR_METATYPE_CONTRIBUTOR_PICTURE; + break; + case FLAC__STREAM_METADATA_PICTURE_TYPE_RECORDING_LOCATION: + case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_RECORDING: + case FLAC__STREAM_METADATA_PICTURE_TYPE_DURING_PERFORMANCE: + case FLAC__STREAM_METADATA_PICTURE_TYPE_VIDEO_SCREEN_CAPTURE: + type = EXTRACTOR_METATYPE_EVENT_PICTURE; + break; + case FLAC__STREAM_METADATA_PICTURE_TYPE_BAND_LOGOTYPE: + case FLAC__STREAM_METADATA_PICTURE_TYPE_PUBLISHER_LOGOTYPE: + type = EXTRACTOR_METATYPE_LOGO; + break; + case FLAC__STREAM_METADATA_PICTURE_TYPE_LEAFLET_PAGE: + case FLAC__STREAM_METADATA_PICTURE_TYPE_MEDIA: + case FLAC__STREAM_METADATA_PICTURE_TYPE_FISH: + case FLAC__STREAM_METADATA_PICTURE_TYPE_ILLUSTRATION: + default: + type = EXTRACTOR_METATYPE_PICTURE; + break; } + ec->proc (ec->cls, + "flac", + type, + EXTRACTOR_METAFORMAT_BINARY, + metadata->data.picture.mime_type, + (const char*) metadata->data.picture.data, + metadata->data.picture.data_length)) break; } case FLAC__METADATA_TYPE_PADDING: @@ -294,81 +398,73 @@ flac_metadata(const FLAC__StreamDecoder *decoder, } } + +/** + * Function called whenever FLAC decoder has trouble. Does nothing. + * + * @param decoder the decoder handle + * @param status type of the error + * @param client_data our 'struct EXTRACTOR_ExtractContext' + */ static void -flac_error(const FLAC__StreamDecoder *decoder, FLAC__StreamDecoderErrorStatus status, void *client_data) +flac_error (const FLAC__StreamDecoder *decoder, + FLAC__StreamDecoderErrorStatus status, + void *client_data) { -#if 0 - fprintf(stderr, - "Got error: %u\n", status); -#endif + /* ignore errors */ } -/* mimetype = audio/flac */ -int -EXTRACTOR_flac_extract (const char *data, - size_t size, - EXTRACTOR_MetaDataProcessor proc, - void *proc_cls, - const char *options) + +/** + * Main entry method for the 'audio/flac' extraction plugin. + * + * @param ec extraction context provided to the plugin + */ +void +EXTRACTOR_flac_extract_method (struct EXTRACTOR_ExtractContext *ec) { FLAC__StreamDecoder * decoder; - struct Context le_cls; - struct Context *ctx; - - if (size < strlen(FLAC_HEADER) + sizeof (int)) - return 0; - if (0 != memcmp(FLAC_HEADER, - data, - strlen(FLAC_HEADER))) - return 0; - decoder = FLAC__stream_decoder_new(); - if (NULL == decoder) - return 0; - FLAC__stream_decoder_set_md5_checking(decoder, false); - FLAC__stream_decoder_set_metadata_ignore_all(decoder); - if (false == FLAC__stream_decoder_set_metadata_respond_all(decoder)) + + if (NULL == (decoder = FLAC__stream_decoder_new ())) + return; + FLAC__stream_decoder_set_md5_checking (decoder, false); + FLAC__stream_decoder_set_metadata_ignore_all (decoder); + if (false == FLAC__stream_decoder_set_metadata_respond_all (decoder)) { - FLAC__stream_decoder_delete(decoder); - return 0; + FLAC__stream_decoder_delete (decoder); + return; } - le_cls.ret = 0; - le_cls.size = size; - le_cls.data = data; - le_cls.proc = proc; - le_cls.proc_cls = proc_cls; - le_cls.pos = 0; if (FLAC__STREAM_DECODER_INIT_STATUS_OK != - FLAC__stream_decoder_init_stream(decoder, - &flac_read, - &flac_seek, - &flac_tell, - &flac_length, - &flac_eof, - &flac_write, - &flac_metadata, - &flac_error, - &le_cls)) + FLAC__stream_decoder_init_stream (decoder, + &flac_read, + &flac_seek, + &flac_tell, + &flac_length, + &flac_eof, + &flac_write, + &flac_metadata, + &flac_error, + ec)) { - FLAC__stream_decoder_delete(decoder); - return le_cls.ret; + FLAC__stream_decoder_delete (decoder); + return; } - if (FLAC__stream_decoder_get_state(decoder) != FLAC__STREAM_DECODER_SEARCH_FOR_METADATA) + if (FLAC__STREAM_DECODER_SEARCH_FOR_METADATA != FLAC__stream_decoder_get_state(decoder)) { - FLAC__stream_decoder_delete(decoder); - return le_cls.ret; + FLAC__stream_decoder_delete (decoder); + return; } if (! FLAC__stream_decoder_process_until_end_of_metadata(decoder)) { - FLAC__stream_decoder_delete(decoder); - return le_cls.ret; + FLAC__stream_decoder_delete (decoder); + return; } - switch (FLAC__stream_decoder_get_state(decoder)) + switch (FLAC__stream_decoder_get_state (decoder)) { case FLAC__STREAM_DECODER_SEARCH_FOR_FRAME_SYNC: case FLAC__STREAM_DECODER_READ_METADATA: case FLAC__STREAM_DECODER_END_OF_STREAM: case FLAC__STREAM_DECODER_READ_FRAME: - ctx = &le_cls; ADD (EXTRACTOR_METATYPE_MIMETYPE, "audio/flac"); break; default: @@ -376,6 +472,8 @@ EXTRACTOR_flac_extract (const char *data, break; } FLAC__stream_decoder_finish (decoder); - FLAC__stream_decoder_delete(decoder); - return le_cls.ret; + FLAC__stream_decoder_delete (decoder); } + +/* end of flac_extractor.c */ + diff --git a/src/plugins/ogg_extractor.c b/src/plugins/ogg_extractor.c @@ -82,7 +82,9 @@ seek_ogg (void *datasource, int whence) { struct EXTRACTOR_ExtractContext *ec = datasource; - int64_t new_position = ec->seek (ec->cls, (int64_t) offset, whence); + int64_t new_position; + + new_position = ec->seek (ec->cls, (int64_t) offset, whence); return (long) new_position; }