6cabfa89a211edaf33156b3a4a8af77b4757840a
[lms] / lightmediascanner / src / plugins / id3 / id3.c
1 /**
2  * Copyright (C) 2008 by INdT
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  *
18  * @author Andre Moreira Magalhaes <andre.magalhaes@openbossa.org>
19  */
20
21 /**
22  * @brief
23  *
24  * id3 file parser.
25  */
26
27 #ifdef HAVE_CONFIG_H
28 #include "config.h"
29 #endif
30
31 #define _XOPEN_SOURCE 600
32 #include <lightmediascanner_plugin.h>
33 #include <lightmediascanner_db.h>
34 #include <sys/types.h>
35 #include <sys/stat.h>
36 #include <fcntl.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40 #include <unistd.h>
41 #include <ctype.h>
42
43 #define ID3V2_GET_FRAME_INFO(frame_data, frame_size, str, len) { \
44     str = malloc(sizeof(char) * (frame_size + 1)); \
45     memcpy(str, frame_data, frame_size); \
46     str[frame_size] = '\0'; \
47     len = frame_size; \
48 }
49
50 #define ID3V2_HEADER_SIZE       10
51 #define ID3V2_FOOTER_SIZE       10
52 #define ID3V2_FRAME_HEADER_SIZE 10
53
54 #define ID3V1_NUM_GENRES 148
55
56 static const char *id3v1_genres[ID3V1_NUM_GENRES] = {
57     "Blues",
58     "Classic Rock",
59     "Country",
60     "Dance",
61     "Disco",
62     "Funk",
63     "Grunge",
64     "Hip-Hop",
65     "Jazz",
66     "Metal",
67     "New Age",
68     "Oldies",
69     "Other",
70     "Pop",
71     "R&B",
72     "Rap",
73     "Reggae",
74     "Rock",
75     "Techno",
76     "Industrial",
77     "Alternative",
78     "Ska",
79     "Death Metal",
80     "Pranks",
81     "Soundtrack",
82     "Euro-Techno",
83     "Ambient",
84     "Trip-Hop",
85     "Vocal",
86     "Jazz+Funk",
87     "Fusion",
88     "Trance",
89     "Classical",
90     "Instrumental",
91     "Acid",
92     "House",
93     "Game",
94     "Sound Clip",
95     "Gospel",
96     "Noise",
97     "Alternative Rock",
98     "Bass",
99     "Soul",
100     "Punk",
101     "Space",
102     "Meditative",
103     "Instrumental Pop",
104     "Instrumental Rock",
105     "Ethnic",
106     "Gothic",
107     "Darkwave",
108     "Techno-Industrial",
109     "Electronic",
110     "Pop-Folk",
111     "Eurodance",
112     "Dream",
113     "Southern Rock",
114     "Comedy",
115     "Cult",
116     "Gangsta",
117     "Top 40",
118     "Christian Rap",
119     "Pop/Funk",
120     "Jungle",
121     "Native American",
122     "Cabaret",
123     "New Wave",
124     "Psychedelic",
125     "Rave",
126     "Showtunes",
127     "Trailer",
128     "Lo-Fi",
129     "Tribal",
130     "Acid Punk",
131     "Acid Jazz",
132     "Polka",
133     "Retro",
134     "Musical",
135     "Rock & Roll",
136     "Hard Rock",
137     "Folk",
138     "Folk/Rock",
139     "National Folk",
140     "Swing",
141     "Fusion",
142     "Bebob",
143     "Latin",
144     "Revival",
145     "Celtic",
146     "Bluegrass",
147     "Avantgarde",
148     "Gothic Rock",
149     "Progressive Rock",
150     "Psychedelic Rock",
151     "Symphonic Rock",
152     "Slow Rock",
153     "Big Band",
154     "Chorus",
155     "Easy Listening",
156     "Acoustic",
157     "Humour",
158     "Speech",
159     "Chanson",
160     "Opera",
161     "Chamber Music",
162     "Sonata",
163     "Symphony",
164     "Booty Bass",
165     "Primus",
166     "Porn Groove",
167     "Satire",
168     "Slow Jam",
169     "Club",
170     "Tango",
171     "Samba",
172     "Folklore",
173     "Ballad",
174     "Power Ballad",
175     "Rhythmic Soul",
176     "Freestyle",
177     "Duet",
178     "Punk Rock",
179     "Drum Solo",
180     "A Cappella",
181     "Euro-House",
182     "Dance Hall",
183     "Goa",
184     "Drum & Bass",
185     "Club-House",
186     "Hardcore",
187     "Terror",
188     "Indie",
189     "BritPop",
190     "Negerpunk",
191     "Polsk Punk",
192     "Beat",
193     "Christian Gangsta Rap",
194     "Heavy Metal",
195     "Black Metal",
196     "Crossover",
197     "Contemporary Christian",
198     "Christian Rock",
199     "Merengue",
200     "Salsa",
201     "Thrash Metal",
202     "Anime",
203     "Jpop",
204     "Synthpop"
205 };
206
207 struct id3v2_frame_header {
208     char frame_id[4];
209     unsigned int frame_size;
210     int compression;
211     int data_length_indicator;
212 };
213
214 struct id3v1_tag {
215     char title[30];
216     char artist[30];
217     char album[30];
218     char year[4];
219     char comments[30];
220     char genre;
221 } __attribute__((packed));
222
223 struct plugin {
224     struct lms_plugin plugin;
225     lms_db_audio_t *audio_db;
226 };
227
228 static const char _name[] = "id3";
229 static const struct lms_string_size _exts[] = {
230     LMS_STATIC_STRING_SIZE(".mp3"),
231     LMS_STATIC_STRING_SIZE(".aac")
232 };
233
234 static int
235 _is_id3v2_second_synch_byte(unsigned char byte)
236 {
237     if (byte == 0xff)
238         return 0;
239     if ((byte & 0xE0) == 0xE0)
240         return 1;
241     return 0;
242 }
243
244 static long
245 _find_id3v2(int fd)
246 {
247     long buffer_offset = 0;
248     char buffer[3], *p;
249     int buffer_size = sizeof(buffer);
250     const char pattern[] = "ID3";
251     ssize_t nread;
252
253     /* These variables are used to keep track of a partial match that happens at
254      * the end of a buffer. */
255     int previous_partial_match = -1;
256     int previous_partial_synch_match = 0;
257     int first_synch_byte;
258
259     /* Start the search at the beginning of the file. */
260     lseek(fd, 0, SEEK_SET);
261
262     if ((nread = read(fd, &buffer, buffer_size)) != buffer_size)
263         return -1;
264
265     /* check if pattern is in the beggining of the file */
266     if (memcmp(buffer, pattern, 3) == 0)
267         return 0;
268
269     /* This loop is the crux of the find method.  There are three cases that we
270      * want to account for:
271      * (1) The previously searched buffer contained a partial match of the search
272      * pattern and we want to see if the next one starts with the remainder of
273      * that pattern.
274      *
275      * (2) The search pattern is wholly contained within the current buffer.
276      *
277      * (3) The current buffer ends with a partial match of the pattern.  We will
278      * note this for use in the next iteration, where we will check for the rest
279      * of the pattern. */
280     while (1) {
281         /* (1) previous partial match */
282         if (previous_partial_synch_match && _is_id3v2_second_synch_byte(buffer[0]))
283             return -1;
284
285         if (previous_partial_match >= 0 && previous_partial_match < buffer_size) {
286             const int pattern_offset = buffer_size - previous_partial_match;
287
288             if (memcmp(buffer, pattern + pattern_offset, 3 - pattern_offset) == 0)
289                 return buffer_offset - buffer_size + previous_partial_match;
290         }
291
292         /* (2) pattern contained in current buffer */
293         p = buffer;
294         while ((p = memchr(p, 'I', buffer_size))) {
295             if (memcmp(p, pattern, 3) == 0)
296                 return buffer_offset + (p - buffer);
297             p += 1;
298         }
299
300         p = memchr(buffer, 255, buffer_size);
301         if (p)
302             first_synch_byte = p - buffer;
303         else
304             first_synch_byte = -1;
305
306         /* Here we have to loop because there could be several of the first
307          * (11111111) byte, and we want to check all such instances until we find
308          * a full match (11111111 111) or hit the end of the buffer. */
309         while (first_synch_byte >= 0) {
310             /* if this *is not* at the end of the buffer */
311             if (first_synch_byte < buffer_size - 1) {
312                 if(_is_id3v2_second_synch_byte(buffer[first_synch_byte + 1]))
313                     /* We've found the frame synch pattern. */
314                     return -1;
315                 else
316                     /* We found 11111111 at the end of the current buffer indicating a
317                      * partial match of the synch pattern.  The find() below should
318                      * return -1 and break out of the loop. */
319                     previous_partial_synch_match = 1;
320             }
321
322             /* Check in the rest of the buffer. */
323             p = memchr(p + 1, 255, buffer_size);
324             if (p)
325                 first_synch_byte = p - buffer;
326             else
327                 first_synch_byte = -1;
328         }
329
330         /* (3) partial match */
331         if (buffer[nread - 1] == pattern[1])
332             previous_partial_match = nread - 1;
333         else if (memcmp(&buffer[nread - 2], pattern, 2) == 0)
334             previous_partial_match = nread - 2;
335         buffer_offset += buffer_size;
336
337         if ((nread = read(fd, &buffer, sizeof(buffer))) == -1)
338             return -1;
339     }
340
341     return -1;
342 }
343
344 static unsigned int
345 _to_uint(const char *data, int data_size)
346 {
347     unsigned int sum = 0;
348     int last = data_size > 4 ? 3 : data_size - 1;
349     int i;
350
351     for (i = 0; i <= last; i++)
352         sum |= (data[i] & 0x7f) << ((last - i) * 7);
353
354     return sum;
355 }
356
357 static unsigned int
358 _get_id3v2_frame_header_size(unsigned int version)
359 {
360     switch (version) {
361     case 0:
362     case 1:
363     case 2:
364         return 6;
365     case 3:
366     case 4:
367     default:
368         return 10;
369     }
370 }
371
372 static void
373 _parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
374 {
375     switch (version) {
376     case 0:
377     case 1:
378     case 2:
379         memcpy(fh->frame_id, data, 3);
380         fh->frame_id[3] = 0;
381         fh->frame_size = _to_uint(data + 3, 3);
382         fh->compression = 0;
383         fh->data_length_indicator = 0;
384         break;
385     case 3:
386         memcpy(fh->frame_id, data, 4);
387         fh->frame_size = _to_uint(data + 4, 4);
388         fh->compression = data[9] & 0x40;
389         fh->data_length_indicator = 0;
390         break;
391     case 4:
392     default:
393         memcpy(fh->frame_id, data, 4);
394         fh->frame_size = _to_uint(data + 4, 4);
395         fh->compression = data[9] & 0x4;
396         fh->data_length_indicator = data[9] & 0x1;
397         break;
398     }
399 }
400
401 static void
402 _parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct lms_audio_info *info)
403 {
404     unsigned int frame_size;
405
406     /* TODO proper handle text encoding
407      *
408      * Latin1  = 0
409      * UTF16   = 1
410      * UTF16BE = 2
411      * UTF8    = 3
412      * UTF16LE = 4
413      */
414
415     /* skip first byte - text encoding */
416     frame_data += 1;
417     frame_size = fh->frame_size - 1;
418
419     if (memcmp(fh->frame_id, "TIT2", 4) == 0)
420         ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->title.str, info->title.len)
421     else if (memcmp(fh->frame_id, "TPE1", 4) == 0)
422         ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->artist.str, info->artist.len)
423     else if (memcmp(fh->frame_id, "TALB", 4) == 0)
424         ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->album.str, info->album.len)
425     else if (memcmp(fh->frame_id, "TCON", 4) == 0) {
426         /* TODO handle multiple genres */
427         int i, is_number;
428
429         ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->genre.str, info->genre.len)
430
431         is_number = 1;
432         for (i = 0; i < info->genre.len; ++i) {
433             if (!isdigit(info->genre.str[i]))
434                 is_number = 0;
435         }
436
437         if ((is_number) &&
438             (info->genre.str) &&
439             (info->genre.len > 0)) {
440             int genre = atoi(info->genre.str);
441             if (genre >= 0 && genre < ID3V1_NUM_GENRES) {
442                 free(info->genre.str);
443                 info->genre.str = strdup(id3v1_genres[(int) genre]);
444                 info->genre.len = strlen(info->genre.str);
445             }
446             else {
447                 /* ignore other genres */
448                 free(info->genre.str);
449                 info->genre.str = NULL;
450                 info->genre.len = 0;
451             }
452         }
453     }
454     else if (memcmp(fh->frame_id, "TRCK", 4) == 0) {
455         char *trackno;
456         unsigned int trackno_len;
457         ID3V2_GET_FRAME_INFO(frame_data, frame_size, trackno, trackno_len);
458         info->trackno = atoi(trackno);
459     }
460 }
461
462 static int
463 _parse_id3v2(int fd, long id3v2_offset, struct lms_audio_info *info)
464 {
465     char header_data[10], frame_header_data[10];
466     unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
467     int extended_header, footer_present;
468     struct id3v2_frame_header fh;
469
470     lseek(fd, id3v2_offset, SEEK_SET);
471
472     /* parse header */
473     if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
474         return -1;
475
476     tag_size = _to_uint(header_data + 6, 4);
477     if (tag_size == 0)
478         return -1;
479
480     /* parse frames */
481     major_version = header_data[3];
482
483     frame_data_pos = 0;
484     frame_data_length = tag_size;
485
486     /* check for extended header */
487     extended_header = header_data[5] & 0x20; /* bit 6 */
488     if (extended_header) {
489         /* skip extended header */
490         unsigned int extended_header_size;
491         char extended_header_data[4];
492
493         if (read(fd, extended_header_data, 4) != 4)
494             return -1;
495         extended_header_size = _to_uint(extended_header_data, 4);
496         lseek(fd, extended_header_size - 4, SEEK_CUR);
497         frame_data_pos += extended_header_size;
498         frame_data_length -= extended_header_size;
499     }
500
501     footer_present = header_data[5] & 0x8;   /* bit 4 */
502     if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
503         frame_data_length -= ID3V2_FOOTER_SIZE;
504
505     frame_header_size = _get_id3v2_frame_header_size(major_version);
506     while (frame_data_pos < frame_data_length - frame_header_size) {
507         if (read(fd, frame_header_data, ID3V2_FRAME_HEADER_SIZE) != ID3V2_FRAME_HEADER_SIZE)
508             return -1;
509
510         if (frame_header_data[0] == 0)
511             break;
512
513         _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
514
515         if (!fh.compression &&
516             fh.frame_id[0] == 'T' &&
517             memcmp(fh.frame_id, "TXXX", 4) != 0) {
518             char *frame_data;
519
520             if (fh.data_length_indicator)
521                 lseek(fd, 4, SEEK_CUR);
522
523             frame_data = malloc(sizeof(char) * fh.frame_size);
524             if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
525                 free(frame_data);
526                 return -1;
527             }
528
529             _parse_id3v2_frame(&fh, frame_data, info);
530             free(frame_data);
531         }
532         else {
533             if (fh.data_length_indicator)
534                 lseek(fd, fh.frame_size + 4, SEEK_CUR);
535             else
536                 lseek(fd, fh.frame_size, SEEK_CUR);
537         }
538
539         frame_data_pos += fh.frame_size + frame_header_size;
540     }
541
542     return 0;
543 }
544
545 static int
546 _parse_id3v1(int fd, struct lms_audio_info *info)
547 {
548     struct id3v1_tag tag;
549     if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
550         return -1;
551
552     info->title.str = strdup(tag.title);
553     info->title.len = strlen(info->title.str);
554     info->artist.str = strdup(tag.artist);
555     info->artist.len = strlen(info->artist.str);
556     info->album.str = strdup(tag.album);
557     info->album.len = strlen(info->album.str);
558     info->genre.str = strdup(id3v1_genres[(int) tag.genre]);
559     info->genre.len = strlen(info->genre.str);
560     if (tag.comments[28] == 0 && tag.comments[29] != 0)
561         info->trackno = (unsigned char) tag.comments[29];
562
563     return 0;
564 }
565
566 static void *
567 _match(struct plugin *p, const char *path, int len, int base)
568 {
569     int i;
570
571     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
572     if (i < 0)
573       return NULL;
574     else
575       return (void*)(i + 1);
576 }
577
578 static int
579 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
580 {
581     struct lms_audio_info info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
582     int r, fd;
583     long id3v2_offset;
584
585     fd = open(finfo->path, O_RDONLY);
586     if (fd < 0) {
587         perror("open");
588         return -1;
589     }
590
591     id3v2_offset = _find_id3v2(fd);
592     if (id3v2_offset >= 0) {
593 #if 0
594         fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
595                 finfo->path, id3v2_offset);
596 #endif
597         if (_parse_id3v2(fd, id3v2_offset, &info) != 0) {
598             r = -2;
599             goto done;
600         }
601     }
602     else {
603         char tag[3];
604 #if 0
605         fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n", finfo->path);
606 #endif
607         /* check for id3v1 tag */
608         if (lseek(fd, -128, SEEK_END) == -1) {
609             r = -3;
610             goto done;
611         }
612
613         if (read(fd, &tag, 3) == -1) {
614             r = -4;
615             goto done;
616         }
617
618         if (memcmp(tag, "TAG", 3) == 0) {
619 #if 0
620             fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
621 #endif
622             if (_parse_id3v1(fd, &info) != 0) {
623                 r = -5;
624                 goto done;
625             }
626         }
627     }
628
629     lms_string_size_strip_and_free(&info.title);
630     lms_string_size_strip_and_free(&info.artist);
631     lms_string_size_strip_and_free(&info.album);
632     lms_string_size_strip_and_free(&info.genre);
633
634     if (!info.title.str) {
635         int ext_idx;
636         ext_idx = ((int)match) - 1;
637         info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
638         info.title.str = malloc((info.title.len + 1) * sizeof(char));
639         memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
640         info.title.str[info.title.len] = '\0';
641     }
642     lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
643
644     if (info.artist.str)
645         lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
646     if (info.album.str)
647         lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
648     if (info.genre.str)
649         lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
650
651 #if 0
652     fprintf(stderr, "file %s info\n", finfo->path);
653     fprintf(stderr, "\ttitle='%s'\n", info.title.str);
654     fprintf(stderr, "\tartist='%s'\n", info.artist.str);
655     fprintf(stderr, "\talbum='%s'\n", info.album.str);
656     fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
657     fprintf(stderr, "\ttrack number='%d'\n", info.trackno);
658 #endif
659
660     info.id = finfo->id;
661     r = lms_db_audio_add(plugin->audio_db, &info);
662
663   done:
664     close(fd);
665
666     if (info.title.str)
667         free(info.title.str);
668     if (info.artist.str)
669         free(info.artist.str);
670     if (info.album.str)
671         free(info.album.str);
672     if (info.genre.str)
673         free(info.genre.str);
674
675     return r;
676 }
677
678 static int
679 _setup(struct plugin *plugin, struct lms_context *ctxt)
680 {
681     plugin->audio_db = lms_db_audio_new(ctxt->db);
682     if (!plugin->audio_db)
683         return -1;
684     return 0;
685 }
686
687 static int
688 _start(struct plugin *plugin, struct lms_context *ctxt)
689 {
690     return lms_db_audio_start(plugin->audio_db);
691 }
692
693 static int
694 _finish(struct plugin *plugin, struct lms_context *ctxt)
695 {
696     if (plugin->audio_db)
697         lms_db_audio_free(plugin->audio_db);
698     return 0;
699 }
700
701 static int
702 _close(struct plugin *plugin)
703 {
704     free(plugin);
705     return 0;
706 }
707
708 API struct lms_plugin *
709 lms_plugin_open(void)
710 {
711     struct plugin *plugin;
712
713     plugin = (struct plugin *)malloc(sizeof(*plugin));
714     plugin->plugin.name = _name;
715     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
716     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
717     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
718     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
719     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
720     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
721
722     return (struct lms_plugin *)plugin;
723 }