2 * Copyright (C) 2008-2011 by ProFUSION embedded systems
3 * Copyright (C) 2008 by INdT
5 * This library is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU Lesser General Public License
7 * as published by the Free Software Foundation; either version 2.1 of
8 * the License, or (at your option) any later version.
10 * This library 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 * Lesser General Public License for more details.
15 * You should have received a copy of the GNU Lesser General Public
16 * License along with this library; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
20 * @author Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
21 * @author Gustavo Sverzut Barbieri <barbieri@profusion.mobi>
35 #define _XOPEN_SOURCE 600
36 #include <lightmediascanner_plugin.h>
37 #include <lightmediascanner_db.h>
38 #include <lightmediascanner_charset_conv.h>
39 #include <sys/types.h>
48 #define ID3V2_HEADER_SIZE 10
49 #define ID3V2_FOOTER_SIZE 10
51 /* We parse only the first 4 bytes, which are the interesting ones */
52 #define MPEG_HEADER_SIZE 4
54 enum mpeg_audio_version {
57 MPEG_AUDIO_VERSION_2_5,
61 enum mpeg_audio_layer {
69 enum mpeg_audio_version version;
70 enum mpeg_audio_layer layer;
73 uint8_t sampling_rate_idx;
77 static const struct lms_string_size _codecs[] = {
79 [0] = LMS_STATIC_STRING_SIZE("mpeg1layer1"),
80 [1] = LMS_STATIC_STRING_SIZE("mpeg1layer2"),
81 [2] = LMS_STATIC_STRING_SIZE("mpeg1layer3"),
82 [3] = LMS_STATIC_STRING_SIZE("mpeg2layer1"),
83 [4] = LMS_STATIC_STRING_SIZE("mpeg2layer2"),
84 [5] = LMS_STATIC_STRING_SIZE("mpeg2layer3"),
85 [6] = LMS_STATIC_STRING_SIZE("mpeg2.5layer1"),
86 [7] = LMS_STATIC_STRING_SIZE("mpeg2.5layer2"),
87 [8] = LMS_STATIC_STRING_SIZE("mpeg2.5layer3"),
90 #define MPEG_CODEC_AAC_START 9
91 [9] = LMS_STATIC_STRING_SIZE("mpeg2aac-main"),
92 [10] = LMS_STATIC_STRING_SIZE("mpeg2aac-lc"),
93 [11] = LMS_STATIC_STRING_SIZE("mpeg2aac-ssr"),
94 [12] = LMS_STATIC_STRING_SIZE("mpeg2aac-ltp"),
96 [13] = LMS_STATIC_STRING_SIZE("mpeg4aac-main"),
97 [14] = LMS_STATIC_STRING_SIZE("mpeg4aac-lc"),
98 [15] = LMS_STATIC_STRING_SIZE("mpeg4aac-ssr"),
99 [16] = LMS_STATIC_STRING_SIZE("mpeg4aac-ltp"),
103 /* Ordered according to AAC index, take care with mp3 */
104 static int _sample_rates[16] = {
107 /* Frequencies available on mp3, */
112 7350, /* reserved, zeroed */
116 ID3_ENCODING_LATIN1 = 0,
118 ID3_ENCODING_UTF16BE,
120 ID3_ENCODING_UTF16LE,
123 #define ID3_NUM_ENCODINGS ID3_ENCODING_LAST
126 #include "id3v1_genres.c"
129 struct lms_string_size title;
130 struct lms_string_size artist;
131 struct lms_string_size album;
132 struct lms_string_size genre;
134 int cur_artist_priority;
137 struct id3v2_frame_header {
139 unsigned int frame_size;
141 int data_length_indicator;
151 } __attribute__((packed));
154 struct lms_plugin plugin;
155 lms_db_audio_t *audio_db;
156 lms_charset_conv_t *cs_convs[ID3_NUM_ENCODINGS];
159 static const char _name[] = "id3";
160 static const struct lms_string_size _exts[] = {
161 LMS_STATIC_STRING_SIZE(".mp3"),
162 LMS_STATIC_STRING_SIZE(".aac")
164 static const char *_cats[] = {
169 static const char *_authors[] = {
170 "Andre Moreira Magalhaes",
171 "Gustavo Sverzut Barbieri",
176 _to_uint(const char *data, int data_size)
178 unsigned int sum = 0;
179 unsigned int last, i;
181 last = data_size > 4 ? 3 : data_size - 1;
183 for (i = 0; i <= last; i++)
184 sum |= ((unsigned char) data[i]) << ((last - i) * 8);
190 _is_id3v2_second_synch_byte(unsigned char byte)
194 if ((byte & 0xE0) == 0xE0)
200 _fill_mp3_header(struct mpeg_header *hdr, const uint8_t b[4])
202 hdr->sampling_rate_idx = (b[2] & 0x0C) >> 2;
203 if (hdr->sampling_rate_idx == 0x3)
206 * Sampling rate frequency index
207 * bits MPEG1 MPEG2 MPEG2.5
208 * 00 44100 Hz 22050 Hz 11025 Hz
209 * 01 48000 Hz 24000 Hz 12000 Hz
210 * 10 32000 Hz 16000 Hz 8000 Hz
211 * 11 reserv. reserv. reserv.
214 /* swap 0x1 and 0x0 */
215 if (hdr->sampling_rate_idx < 0x2)
216 hdr->sampling_rate_idx = !hdr->sampling_rate_idx;
217 hdr->sampling_rate_idx += 3 * hdr->version + 3;
219 hdr->codec_idx = hdr->version * 3 + hdr->layer;
221 hdr->channels = (b[3] & 0xC0) >> 6;
222 hdr->channels = hdr->channels == 0x3 ? 1 : 2;
227 _fill_aac_header(struct mpeg_header *hdr, const uint8_t b[4])
229 unsigned int profile;
231 hdr->sampling_rate_idx = (b[2] & 0x3C) >> 2;
233 profile = (b[2] & 0xC0) >> 6;
234 hdr->codec_idx = MPEG_CODEC_AAC_START + profile;
235 if (hdr->version == MPEG_AUDIO_VERSION_4)
238 hdr->channels = ((b[2] & 0x1) << 2) | ((b[3] & 0xC0) >> 6);
243 _fill_mpeg_header(struct mpeg_header *hdr, const uint8_t b[4])
245 unsigned int version = (b[1] & 0x18) >> 3;
246 unsigned int layer = (b[1] & 0x06) >> 1;
250 if (version == 0x2 || version == 0x3)
251 hdr->layer = MPEG_AUDIO_LAYER_AAC;
256 hdr->layer = MPEG_AUDIO_LAYER_3;
259 hdr->layer = MPEG_AUDIO_LAYER_2;
262 hdr->layer = MPEG_AUDIO_LAYER_1;
268 hdr->version = MPEG_AUDIO_VERSION_2_5;
274 hdr->version = MPEG_AUDIO_VERSION_4;
276 hdr->version = MPEG_AUDIO_VERSION_2;
280 hdr->version = MPEG_AUDIO_VERSION_2;
282 hdr->version = MPEG_AUDIO_VERSION_1;
285 if (hdr->layer == MPEG_AUDIO_LAYER_AAC)
286 return _fill_aac_header(hdr, b);
288 return _fill_mp3_header(hdr, b);
294 _parse_mpeg_header(int fd, off_t off, struct lms_audio_info *audio_info)
297 const uint8_t *p, *p_end;
298 unsigned int prev_read;
299 struct mpeg_header hdr;
301 lseek(fd, off, SEEK_SET);
306 int nread = read(fd, buffer + prev_read, sizeof(buffer) - prev_read);
307 if (nread < MPEG_HEADER_SIZE)
311 p_end = buffer + nread;
312 while (p < p_end && (p = memchr(p, 0xFF, p_end - p))) {
313 /* poor man's ring buffer since the the needle is small (4 bytes) */
314 if (p > p_end - MPEG_HEADER_SIZE) {
315 memcpy(buffer, p, p_end - p);
319 if (_is_id3v2_second_synch_byte(*(p + 1)))
324 prev_read = p ? p_end - p : 0;
329 if (_fill_mpeg_header(&hdr, p) < 0) {
330 fprintf(stderr, "Invalid field in file, ignoring.\n");
334 audio_info->codec = _codecs[hdr.codec_idx];
335 audio_info->sampling_rate = _sample_rates[hdr.sampling_rate_idx];
336 audio_info->channels = hdr.channels;
341 /* Returns the offset in fd to the position after the ID3 tag, iff it occurs
342 * *before* a sync word. Otherwise < 0 is returned and if we gave up looking
343 * after ID3 because of a sync value, @syncframe_offset is set to its
344 * correspondent offset */
346 _find_id3v2(int fd, off_t *syncframe_offset)
348 static const char pattern[3] = "ID3";
350 unsigned int prev_part_match, prev_part_match_sync = 0;
353 if (read(fd, buffer, sizeof(buffer)) != sizeof(buffer))
356 if (memcmp(buffer, pattern, sizeof(pattern)) == 0)
359 /* This loop is the crux of the find method. There are three cases that we
360 * want to account for:
361 * (1) The previously searched buffer contained a partial match of the
362 * search pattern and we want to see if the next one starts with the
363 * remainder of that pattern.
365 * (2) The search pattern is wholly contained within the current buffer.
367 * (3) The current buffer ends with a partial match of the pattern. We will
368 * note this for use in the next iteration, where we will check for the rest
372 prev_part_match_sync = 0;
375 const char *p, *p_end;
377 /* (1) previous partial match */
378 if (prev_part_match_sync) {
379 if (_is_id3v2_second_synch_byte(buffer[0])) {
380 *syncframe_offset = buffer_offset - 1;
383 prev_part_match_sync = 0;
386 if (prev_part_match) {
387 const int size = sizeof(buffer) - prev_part_match;
388 const char *part_pattern = pattern + prev_part_match;
390 if (memcmp(buffer, part_pattern, size) == 0)
391 return buffer_offset - prev_part_match;
396 p_end = buffer + sizeof(buffer);
397 for (p = buffer; p < p_end; p++) {
398 if (*p == pattern[0]) {
399 /* Try to match pattern, possible partial contents */
405 if (todo == 0 || memcmp(q, pattern + 1, todo) == 0) {
407 if (todo == sizeof(buffer))
408 /* (2) pattern contained in current buffer */
409 return buffer_offset;
411 /* (3) partial match */
412 prev_part_match = todo;
415 } else if ((unsigned char)*p == 0xff) {
416 /* Try to match synch pattern, possible partial contents */
421 if (_is_id3v2_second_synch_byte(*q)) {
422 /* (2) synch pattern contained in current buffer */
423 *syncframe_offset = buffer_offset + (p - buffer);
427 /* (3) partial match */
428 prev_part_match_sync = 1;
432 if (read(fd, buffer, sizeof(buffer)) != sizeof(buffer))
434 buffer_offset += sizeof(buffer);
441 _get_id3v2_frame_header_size(unsigned int version)
456 _parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
462 memcpy(fh->frame_id, data, 3);
464 fh->frame_size = _to_uint(data + 3, 3);
466 fh->data_length_indicator = 0;
469 memcpy(fh->frame_id, data, 4);
470 fh->frame_size = _to_uint(data + 4, 4);
471 fh->compression = data[9] & 0x40;
472 fh->data_length_indicator = 0;
476 memcpy(fh->frame_id, data, 4);
477 fh->frame_size = _to_uint(data + 4, 4);
478 fh->compression = data[9] & 0x4;
479 fh->data_length_indicator = data[9] & 0x1;
485 _get_id3v2_frame_info(const char *frame_data, unsigned int frame_size, struct lms_string_size *s, lms_charset_conv_t *cs_conv, int strip)
489 if (frame_size > s->len) {
492 tmp = realloc(s->str, sizeof(char) * (frame_size + 1));
497 memcpy(s->str, frame_data, frame_size);
498 s->str[frame_size] = '\0';
501 lms_charset_conv(cs_conv, &s->str, &s->len);
503 lms_string_size_strip_and_free(s);
507 _get_id3v2_artist(unsigned int index, const char *frame_data, unsigned int frame_size, struct id3_info *info, lms_charset_conv_t *cs_conv)
509 static const unsigned char artist_priorities[] = {3, 4, 2, 1};
510 const unsigned int index_max = sizeof(artist_priorities) / sizeof(*artist_priorities);
512 if (index >= index_max)
515 if (artist_priorities[index] > info->cur_artist_priority) {
516 struct lms_string_size artist = {0};
518 _get_id3v2_frame_info(frame_data, frame_size, &artist, cs_conv, 1);
520 if (info->artist.str)
521 free(info->artist.str);
522 info->artist = artist;
523 info->cur_artist_priority = artist_priorities[index];
530 _get_id3v1_genre(unsigned int genre, struct lms_string_size *out)
532 if (genre < ID3V1_NUM_GENRES) {
533 unsigned int size, base, len;
535 base = id3v1_genres_offsets[genre];
536 size = id3v1_genres_offsets[genre + 1] - base;
539 if (len > out->len) {
540 char *p = realloc(out->str, size);
547 memcpy(out->str, id3v1_genres_mem + base, size);
555 _parse_id3v1_genre(const char *str_genre, struct lms_string_size *out)
557 return _get_id3v1_genre(atoi(str_genre), out);
561 _get_id3v2_genre(const char *frame_data, unsigned int frame_size, struct lms_string_size *out, lms_charset_conv_t *cs_conv)
564 struct lms_string_size genre = {0};
566 _get_id3v2_frame_info(frame_data, frame_size, &genre, cs_conv, 1);
570 is_number = (genre.len != 0 && genre.str[0] != '(');
572 for (i = 0; i < genre.len; ++i) {
573 if (!isdigit(genre.str[i])) {
580 if (is_number && _parse_id3v1_genre(genre.str, out) == 0) {
581 /* id3v1 genre found */
586 /* ID3v2.3 "content type" can contain a ID3v1 genre number in parenthesis at
587 * the beginning of the field. If this is all that the field contains, do a
588 * translation from that number to the name and return that. If there is a
589 * string folloing the ID3v1 genre number, that is considered to be
590 * authoritative and we return that instead. Or finally, the field may
591 * simply be free text, in which case we just return the value. */
593 if (genre.len > 1 && genre.str[0] == '(') {
594 char *closing = NULL;
596 if (genre.str[genre.len - 1] == ')') {
597 closing = strchr(genre.str, ')');
598 if (closing == genre.str + genre.len - 1) {
599 /* ) is the last character and only appears once in the
600 * string get the id3v1 genre enclosed by parentheses
602 if (_parse_id3v1_genre(genre.str + 1, out) == 0) {
609 /* get the string followed by the id3v1 genre */
611 closing = strchr(genre.str, ')');
615 out->len = genre.len - (closing - genre.str);
616 out->str = genre.str;
617 memmove(out->str, closing, out->len + 1); /* includes '\0' */
618 lms_string_size_strip_and_free(out);
628 _get_id3v2_trackno(const char *frame_data, unsigned int frame_size, struct id3_info *info, lms_charset_conv_t *cs_conv)
630 struct lms_string_size trackno = {0};
632 _get_id3v2_frame_info(frame_data, frame_size, &trackno, cs_conv, 0);
635 info->trackno = atoi(trackno.str);
640 _parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct id3_info *info, lms_charset_conv_t **cs_convs)
642 lms_charset_conv_t *cs_conv;
643 unsigned int text_encoding, frame_size;
646 /* ignore frames which contains just the encoding */
647 if (fh->frame_size <= 1)
651 fprintf(stderr, "frame id = %.4s frame size = %d text encoding = %d\n",
652 fh->frame_id, fh->frame_size, frame_data[0]);
655 /* All used frames start with 'T' */
666 text_encoding = frame_data[0];
668 /* skip first byte - text encoding */
670 frame_size = fh->frame_size - 1;
672 if (text_encoding < ID3_NUM_ENCODINGS) {
673 if (text_encoding == ID3_ENCODING_UTF16) {
674 /* ignore frames which contains just the encoding */
678 if (memcmp(frame_data, "\xfe\xff", 2) == 0)
679 text_encoding = ID3_ENCODING_UTF16BE;
681 text_encoding = ID3_ENCODING_UTF16LE;
685 cs_conv = cs_convs[text_encoding];
689 /* ID3v2.2 used 3 bytes for the frame id, so let's check it */
690 if ((fid[1] == 'T' && fid[2] == '2') ||
691 (fid[1] == 'I' && fid[2] == 'T' && fid[3] == '2'))
692 _get_id3v2_frame_info(frame_data, frame_size, &info->title, cs_conv, 1);
693 else if (fid[1] == 'P') {
695 _get_id3v2_artist(fid[3] - '1', frame_data, frame_size,
697 else if (fid[2] >= '1' && fid[2] <= '4')
698 _get_id3v2_artist(fid[2] - '1', frame_data, frame_size,
702 else if (fid[1] == 'A' && fid[2] == 'L')
703 _get_id3v2_frame_info(frame_data, frame_size, &info->album, cs_conv, 1);
704 /* TCON (Content/Genre) */
705 else if (fid[1] == 'C' && fid[2] == 'O' && fid[3] == 'N')
706 _get_id3v2_genre(frame_data, frame_size, &info->genre, cs_conv);
707 else if (fid[1] == 'R' && (fid[2] == 'K' ||
708 (fid[2] == 'C' && fid[3] == 'K')))
709 _get_id3v2_trackno(frame_data, frame_size, info, cs_conv);
713 _parse_id3v2(int fd, long id3v2_offset, struct id3_info *info, lms_charset_conv_t **cs_convs)
715 char header_data[10], frame_header_data[10];
716 unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
717 int extended_header, footer_present;
718 struct id3v2_frame_header fh;
721 lseek(fd, id3v2_offset, SEEK_SET);
724 if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
727 tag_size = _to_uint(header_data + 6, 4);
732 major_version = header_data[3];
735 frame_data_length = tag_size;
737 /* check for extended header */
738 extended_header = header_data[5] & 0x20; /* bit 6 */
739 if (extended_header) {
740 /* skip extended header */
741 unsigned int extended_header_size;
742 char extended_header_data[4];
744 if (read(fd, extended_header_data, 4) != 4)
746 extended_header_size = _to_uint(extended_header_data, 4);
747 lseek(fd, extended_header_size - 4, SEEK_CUR);
748 frame_data_pos += extended_header_size;
749 frame_data_length -= extended_header_size;
752 footer_present = header_data[5] & 0x8; /* bit 4 */
753 if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
754 frame_data_length -= ID3V2_FOOTER_SIZE;
756 frame_header_size = _get_id3v2_frame_header_size(major_version);
757 while (frame_data_pos < frame_data_length - frame_header_size) {
758 nread = read(fd, frame_header_data, frame_header_size);
762 if (nread != frame_header_size)
765 if (frame_header_data[0] == 0)
768 _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
770 if (fh.frame_size > 0 &&
772 fh.frame_id[0] == 'T' &&
773 memcmp(fh.frame_id, "TXXX", 4) != 0) {
776 if (fh.data_length_indicator)
777 lseek(fd, 4, SEEK_CUR);
779 frame_data = malloc(sizeof(char) * fh.frame_size);
780 if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
785 _parse_id3v2_frame(&fh, frame_data, info, cs_convs);
789 if (fh.data_length_indicator)
790 lseek(fd, fh.frame_size + 4, SEEK_CUR);
792 lseek(fd, fh.frame_size, SEEK_CUR);
795 frame_data_pos += fh.frame_size + frame_header_size;
802 _id3v1_str_get(struct lms_string_size *s, const char *buf, int maxlen, lms_charset_conv_t *cs_conv)
805 const char *p, *p_end, *p_last;
809 p_end = buf + maxlen;
810 for (p = buf; *p != '\0' && p < p_end; p++) {
820 len = (p_last - buf) - start;
824 len++; /* p_last is not included yet */
828 tmp = realloc(s->str, sizeof(char) * (len + 1));
835 memcpy(s->str, buf + start, len);
839 lms_charset_conv(cs_conv, &s->str, &s->len);
843 _parse_id3v1(int fd, struct id3_info *info, lms_charset_conv_t *cs_conv)
845 struct id3v1_tag tag;
846 if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
849 if (!info->title.str)
850 _id3v1_str_get(&info->title, tag.title, sizeof(tag.title), cs_conv);
851 if (!info->artist.str)
852 _id3v1_str_get(&info->artist, tag.artist, sizeof(tag.artist), cs_conv);
853 if (!info->album.str)
854 _id3v1_str_get(&info->album, tag.album, sizeof(tag.album), cs_conv);
855 if (!info->genre.str)
856 _get_id3v1_genre(tag.genre, &info->genre);
857 if (info->trackno == -1 &&
858 tag.comments[28] == 0 && tag.comments[29] != 0)
859 info->trackno = (unsigned char) tag.comments[29];
865 _match(struct plugin *p, const char *path, int len, int base)
869 i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
873 return (void*)(i + 1);
877 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
879 struct id3_info info = {
881 .cur_artist_priority = -1,
883 struct lms_audio_info audio_info = { };
886 off_t syncframe_offset = 0;
888 fd = open(finfo->path, O_RDONLY);
894 id3v2_offset = _find_id3v2(fd, &syncframe_offset);
895 if (id3v2_offset >= 0) {
897 fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
898 finfo->path, id3v2_offset);
900 if (_parse_id3v2(fd, id3v2_offset, &info, plugin->cs_convs) != 0 ||
901 !info.title.str || !info.artist.str ||
902 !info.album.str || !info.genre.str ||
903 info.trackno == -1) {
905 fprintf(stderr, "id3v2 invalid in file %s\n", finfo->path);
910 /* Even if later we failed to parse the ID3, we want to look for sync
911 * frame only where we were left */
912 syncframe_offset = lseek(fd, 0, SEEK_CUR);
915 if (id3v2_offset < 0) {
918 fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n",
921 /* check for id3v1 tag */
922 if (lseek(fd, -128, SEEK_END) == -1) {
927 if (read(fd, &tag, 3) == -1) {
932 if (memcmp(tag, "TAG", 3) == 0) {
934 fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
936 if (_parse_id3v1(fd, &info, ctxt->cs_conv) != 0) {
943 if (!info.title.str) {
945 ext_idx = ((long)match) - 1;
946 info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
947 info.title.str = malloc((info.title.len + 1) * sizeof(char));
948 memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
949 info.title.str[info.title.len] = '\0';
950 lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
953 if (info.trackno == -1)
957 fprintf(stderr, "file %s info\n", finfo->path);
958 fprintf(stderr, "\ttitle='%s'\n", info.title.str);
959 fprintf(stderr, "\tartist='%s'\n", info.artist.str);
960 fprintf(stderr, "\talbum='%s'\n", info.album.str);
961 fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
962 fprintf(stderr, "\ttrack number='%d'\n", info.trackno);
965 audio_info.id = finfo->id;
966 audio_info.title = info.title;
967 audio_info.artist = info.artist;
968 audio_info.album = info.album;
969 audio_info.genre = info.genre;
970 audio_info.trackno = info.trackno;
972 _parse_mpeg_header(fd, syncframe_offset, &audio_info);
974 r = lms_db_audio_add(plugin->audio_db, &audio_info);
977 posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
981 free(info.title.str);
983 free(info.artist.str);
985 free(info.album.str);
987 free(info.genre.str);
993 _setup(struct plugin *plugin, struct lms_context *ctxt)
996 const char *id3_encodings[ID3_NUM_ENCODINGS] = {
1004 plugin->audio_db = lms_db_audio_new(ctxt->db);
1005 if (!plugin->audio_db)
1008 for (i = 0; i < ID3_NUM_ENCODINGS; ++i) {
1009 /* do not create charset conv for UTF-8 encoding */
1010 if (!id3_encodings[i]) {
1011 plugin->cs_convs[i] = NULL;
1014 plugin->cs_convs[i] = lms_charset_conv_new_full(0, 0);
1015 if (!plugin->cs_convs[i])
1017 lms_charset_conv_add(plugin->cs_convs[i], id3_encodings[i]);
1024 _start(struct plugin *plugin, struct lms_context *ctxt)
1026 return lms_db_audio_start(plugin->audio_db);
1030 _finish(struct plugin *plugin, struct lms_context *ctxt)
1034 if (plugin->audio_db)
1035 lms_db_audio_free(plugin->audio_db);
1037 for (i = 0; i < ID3_NUM_ENCODINGS; ++i) {
1038 if (plugin->cs_convs[i])
1039 lms_charset_conv_free(plugin->cs_convs[i]);
1046 _close(struct plugin *plugin)
1052 API struct lms_plugin *
1053 lms_plugin_open(void)
1055 struct plugin *plugin;
1057 plugin = (struct plugin *)malloc(sizeof(*plugin));
1058 plugin->plugin.name = _name;
1059 plugin->plugin.match = (lms_plugin_match_fn_t)_match;
1060 plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
1061 plugin->plugin.close = (lms_plugin_close_fn_t)_close;
1062 plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
1063 plugin->plugin.start = (lms_plugin_start_fn_t)_start;
1064 plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
1066 return (struct lms_plugin *)plugin;
1069 API struct lms_plugin_info *
1070 lms_plugin_info(void)
1072 static struct lms_plugin_info info = {
1075 "ID3 v1 and v2 for mp3 files",
1078 "http://lms.garage.maemo.org"