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