Removed supported extensions from audio dummy.
[lms] / lightmediascanner / src / plugins / aac / aac.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  * aac 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
42 #define ID3V2_GET_FRAME_INFO(frame_data, frame_size, str, len) { \
43     str = malloc(sizeof(char) * (frame_size + 1)); \
44     memcpy(str, frame_data, frame_size); \
45     str[frame_size] = '\0'; \
46     len = frame_size; \
47 }
48
49 #define ID3V2_HEADER_SIZE       10
50 #define ID3V2_FOOTER_SIZE       10
51 #define ID3V2_FRAME_HEADER_SIZE 10
52
53 #define ID3V1_NUM_GENRES 126
54
55 static const char *id3v1_genres[126] = {
56     "Blues", "Classic Rock", "Country", "Dance", "Disco", "Funk", "Grunge",
57     "Hip-Hop", "Jazz", "Metal", "New Age", "Oldies", "Other", "Pop",
58     "R&B", "Rap", "Reggae", "Rock", "Techno", "Industrial", "Alternative",
59     "Ska", "Death Metal", "Pranks", "Soundtrack", "Euro-Techno", "Ambient",
60     "Trip-Hop", "Vocal", "Jazz+Funk", "Fusion", "Trance", "Classical",
61     "Instrumental", "Acid", "House", "Game", "Sound Clip", "Gospel",
62     "Noise", "AlternRock", "Bass", "Soul", "Punk", "Space", "Meditative",
63     "Instrumental Pop", "Instrumental Rock", "Ethnic", "Gothic",
64     "Darkwave", "Techno-Industrial", "Electronic", "Pop-Folk", "Eurodance",
65     "Dream", "Southern Rock", "Comedy", "Cult", "Gangsta", "Top 40",
66     "Christian Rap", "Pop/Funk", "Jungle", "Native American", "Cabaret",
67     "New Wave", "Psychadelic", "Rave", "Showtunes", "Trailer", "Lo-Fi",
68     "Tribal", "Acid Punk", "Acid Jazz", "Polka", "Retro", "Musical", "Rock & Roll",
69     "Hard Rock", "Folk", "Folk-Rock", "National Folk", "Swing", "Fast Fusion",
70     "Bebob", "Latin", "Revival", "Celtic", "Bluegrass", "Avantgarde", "Gothic Rock",
71     "Progressive Rock", "Psychedelic Rock", "Symphonic Rock", "Slow Rock", "Big Band",
72     "Chorus", "Easy Listening", "Acoustic", "Humour", "Speech", "Chanson",
73     "Opera", "Chamber Music", "Sonata", "Symphony", "Booty Bass", "Primus",
74     "Porn Groove", "Satire", "Slow Jam", "Club", "Tango", "Samba", "Folklore",
75     "Ballad", "Power Ballad", "Rhythmic Soul", "Freestyle", "Duet", "Punk Rock",
76     "Drum Solo", "A capella", "Euro-House", "Dance Hall",
77 };
78
79 struct id3v2_frame_header {
80     char frame_id[4];
81     unsigned int frame_size;
82     int compression;
83     int data_length_indicator;
84 };
85
86 struct id3v1_tag {
87     char title[30];
88     char artist[30];
89     char album[30];
90     char year[4];
91     char comments[30];
92     char genre;
93 } __attribute__((packed));
94
95 struct plugin {
96     struct lms_plugin plugin;
97     lms_db_audio_t *audio_db;
98 };
99
100 static const char _name[] = "aac";
101 static const struct lms_string_size _exts[] = {
102     LMS_STATIC_STRING_SIZE(".aac")
103 };
104
105 static int
106 _is_id3v2_second_synch_byte(unsigned char byte)
107 {
108     if (byte == 0xff)
109         return 0;
110     if ((byte & 0xE0) == 0xE0)
111         return 1;
112     return 0;
113 }
114
115 static long
116 _find_id3v2(int fd)
117 {
118     long buffer_offset = 0;
119     char buffer[1025];
120     char *p;
121     int buffer_size = sizeof(buffer);
122     const char pattern[] = "ID3";
123     ssize_t nread;
124
125     /* These variables are used to keep track of a partial match that happens at
126      * the end of a buffer. */
127     int previous_partial_match = -1;
128     int previous_partial_synch_match = 0;
129     int first_synch_byte;
130
131     /* Start the search at the beginning of the file. */
132     lseek(fd, 0, SEEK_SET);
133
134     /* This loop is the crux of the find method.  There are three cases that we
135      * want to account for:
136      * (1) The previously searched buffer contained a partial match of the search
137      * pattern and we want to see if the next one starts with the remainder of
138      * that pattern.
139      *
140      * (2) The search pattern is wholly contained within the current buffer.
141      *
142      * (3) The current buffer ends with a partial match of the pattern.  We will
143      * note this for use in the next iteration, where we will check for the rest
144      * of the pattern. */
145
146     if ((nread = read(fd, &buffer, sizeof(buffer))) <= 0)
147         return -1;
148     while (1) {
149         /* (1) previous partial match */
150         if (previous_partial_synch_match && _is_id3v2_second_synch_byte(buffer[0]))
151             return -1;
152
153         if (previous_partial_match >= 0 && previous_partial_match < buffer_size) {
154             const int pattern_offset = buffer_size - previous_partial_match;
155
156             if (memcmp(buffer, pattern + pattern_offset, 3 - pattern_offset) == 0)
157                 return buffer_offset - buffer_size + previous_partial_match;
158         }
159
160         /* (2) pattern contained in current buffer */
161         p = buffer;
162         while ((p = memchr(p, 'I', buffer_size))) {
163             if (memcmp(p, pattern, 3) == 0)
164                 return buffer_offset + (p - buffer);
165             p += 1;
166         }
167
168         p = memchr(buffer, 255, buffer_size);
169         if (p)
170             first_synch_byte = p - buffer;
171         else
172             first_synch_byte = -1;
173
174         /* Here we have to loop because there could be several of the first
175          * (11111111) byte, and we want to check all such instances until we find
176          * a full match (11111111 111) or hit the end of the buffer. */
177         while (first_synch_byte >= 0) {
178             /* if this *is not* at the end of the buffer */
179             if (first_synch_byte < buffer_size - 1) {
180                 if(_is_id3v2_second_synch_byte(buffer[first_synch_byte + 1]))
181                     /* We've found the frame synch pattern. */
182                     return -1;
183                 else
184                     /* We found 11111111 at the end of the current buffer indicating a
185                      * partial match of the synch pattern.  The find() below should
186                      * return -1 and break out of the loop. */
187                     previous_partial_synch_match = 1;
188             }
189
190             /* Check in the rest of the buffer. */
191             p = memchr(p + 1, 255, buffer_size);
192             if (p)
193                 first_synch_byte = p - buffer;
194             else
195                 first_synch_byte = -1;
196         }
197
198         /* (3) partial match */
199         if (buffer[nread - 1] == pattern[1])
200             previous_partial_match = nread - 1;
201         else if (memcmp(&buffer[nread - 2], pattern, 2) == 0)
202             previous_partial_match = nread - 2;
203         buffer_offset += buffer_size;
204
205         if ((nread = read(fd, &buffer, sizeof(buffer))) <= 0)
206             return -1;
207     }
208
209     return -1;
210 }
211
212 static unsigned int
213 _to_uint(const char *data, int data_size)
214 {
215     unsigned int sum = 0;
216     int last = data_size > 4 ? 3 : data_size - 1;
217     int i;
218
219     for (i = 0; i <= last; i++)
220         sum |= (data[i] & 0x7f) << ((last - i) * 7);
221
222     return sum;
223 }
224
225 static unsigned int
226 _get_id3v2_frame_header_size(unsigned int version)
227 {
228     switch (version) {
229     case 0:
230     case 1:
231     case 2:
232         return 6;
233     case 3:
234     case 4:
235     default:
236         return 10;
237     }
238 }
239
240 static void
241 _parse_id3v2_frame_header(char *data, unsigned int version, struct id3v2_frame_header *fh)
242 {
243     switch (version) {
244     case 0:
245     case 1:
246     case 2:
247         memcpy(fh->frame_id, data, 3);
248         fh->frame_id[3] = 0;
249         fh->frame_size = _to_uint(data + 3, 3);
250         fh->compression = 0;
251         fh->data_length_indicator = 0;
252         break;
253     case 3:
254         memcpy(fh->frame_id, data, 4);
255         fh->frame_size = _to_uint(data + 4, 4);
256         fh->compression = data[9] & 0x40;
257         fh->data_length_indicator = 0;
258         break;
259     case 4:
260     default:
261         memcpy(fh->frame_id, data, 4);
262         fh->frame_size = _to_uint(data + 4, 4);
263         fh->compression = data[9] & 0x4;
264         fh->data_length_indicator = data[9] & 0x1;
265         break;
266     }
267 }
268
269 static void
270 _parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct lms_audio_info *info)
271 {
272     unsigned int frame_size;
273
274     /* TODO proper handle text encoding
275      *
276      * Latin1  = 0
277      * UTF16   = 1
278      * UTF16BE = 2
279      * UTF8    = 3
280      * UTF16LE = 4
281      */
282
283     /* skip first byte - text encoding */
284     frame_data += 1;
285     frame_size = fh->frame_size - 1;
286
287     if (memcmp(fh->frame_id, "TIT2", 4) == 0)
288         ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->title.str, info->title.len)
289     else if (memcmp(fh->frame_id, "TPE1", 4) == 0)
290         ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->artist.str, info->artist.len)
291     else if (memcmp(fh->frame_id, "TALB", 4) == 0)
292         ID3V2_GET_FRAME_INFO(frame_data, frame_size, info->album.str, info->album.len)
293     else if (memcmp(fh->frame_id, "TRCK", 4) == 0) {
294         char *trackno;
295         unsigned int trackno_len;
296         ID3V2_GET_FRAME_INFO(frame_data, frame_size, trackno, trackno_len);
297         info->trackno = atoi(trackno);
298     }
299 }
300
301 static int
302 _parse_id3v2(int fd, long id3v2_offset, struct lms_audio_info *info)
303 {
304     char header_data[10], frame_header_data[10];
305     unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
306     int extended_header, footer_present;
307     struct id3v2_frame_header fh;
308
309     lseek(fd, id3v2_offset, SEEK_SET);
310
311     /* parse header */
312     if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
313         return -1;
314
315     tag_size = _to_uint(header_data + 6, 4);
316     if (tag_size == 0)
317         return -1;
318
319     /* parse frames */
320     major_version = header_data[3];
321
322     frame_data_pos = 0;
323     frame_data_length = tag_size;
324
325     /* check for extended header */
326     extended_header = header_data[5] & 0x20; /* bit 6 */
327     if (extended_header) {
328         /* skip extended header */
329         unsigned int extended_header_size;
330         char extended_header_data[4];
331
332         if (read(fd, extended_header_data, 4) != 4)
333             return -1;
334         extended_header_size = _to_uint(extended_header_data, 4);
335         lseek(fd, extended_header_size - 4, SEEK_CUR);
336         frame_data_pos += extended_header_size;
337         frame_data_length -= extended_header_size;
338     }
339
340     footer_present = header_data[5] & 0x8;   /* bit 4 */
341     if (footer_present && frame_data_length > ID3V2_FOOTER_SIZE)
342         frame_data_length -= ID3V2_FOOTER_SIZE;
343
344     frame_header_size = _get_id3v2_frame_header_size(major_version);
345     while (frame_data_pos < frame_data_length - frame_header_size) {
346         if (read(fd, frame_header_data, ID3V2_FRAME_HEADER_SIZE) != ID3V2_FRAME_HEADER_SIZE)
347             return -1;
348
349         if (frame_header_data[0] == 0)
350             break;
351
352         _parse_id3v2_frame_header(frame_header_data, major_version, &fh);
353
354         if (!fh.compression &&
355             fh.frame_id[0] == 'T' &&
356             memcmp(fh.frame_id, "TXXX", 4) != 0) {
357             char *frame_data;
358
359             if (fh.data_length_indicator)
360                 lseek(fd, 4, SEEK_CUR);
361
362             frame_data = malloc(sizeof(char) * fh.frame_size);
363             if (read(fd, frame_data, fh.frame_size) != fh.frame_size) {
364                 free(frame_data);
365                 return -1;
366             }
367
368             _parse_id3v2_frame(&fh, frame_data, info);
369             free(frame_data);
370         }
371         else {
372             if (fh.data_length_indicator)
373                 lseek(fd, fh.frame_size + 4, SEEK_CUR);
374             else
375                 lseek(fd, fh.frame_size, SEEK_CUR);
376         }
377
378         frame_data_pos += fh.frame_size + frame_header_size;
379     }
380
381     return 0;
382 }
383
384 static int
385 _parse_id3v1(int fd, struct lms_audio_info *info)
386 {
387     struct id3v1_tag tag;
388     if (read(fd, &tag, sizeof(struct id3v1_tag)) == -1)
389         return -1;
390
391     info->title.str = strdup(tag.title);
392     info->title.len = strlen(info->title.str);
393     info->artist.str = strdup(tag.artist);
394     info->artist.len = strlen(info->artist.str);
395     info->album.str = strdup(tag.album);
396     info->album.len = strlen(info->album.str);
397     if (tag.genre >= 0 && tag.genre < ID3V1_NUM_GENRES) {
398         info->genre.str = strdup(id3v1_genres[(int) tag.genre]);
399         info->genre.len = strlen(info->genre.str);
400     }
401
402     return 0;
403 }
404
405 static void *
406 _match(struct plugin *p, const char *path, int len, int base)
407 {
408     int i;
409
410     i = lms_which_extension(path, len, _exts, LMS_ARRAY_SIZE(_exts));
411     if (i < 0)
412       return NULL;
413     else
414       return (void*)(i + 1);
415 }
416
417 static int
418 _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_info *finfo, void *match)
419 {
420     struct lms_audio_info info = {0, {0}, {0}, {0}, {0}, 0, 0, 0};
421     int r, fd;
422     long id3v2_offset;
423
424     fd = open(finfo->path, O_RDONLY);
425     if (fd < 0) {
426         perror("open");
427         return -1;
428     }
429
430     id3v2_offset = _find_id3v2(fd);
431     if (id3v2_offset >= 0) {
432         fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
433                 finfo->path, id3v2_offset);
434         if (_parse_id3v2(fd, id3v2_offset, &info) != 0) {
435             r = -2;
436             goto done;
437         }
438     }
439     else {
440         char tag[3];
441
442         fprintf(stderr, "id3v2 tag not found in file %s. trying id3v1\n", finfo->path);
443         /* check for id3v1 tag */
444         if (lseek(fd, -128, SEEK_END) == -1) {
445             r = -3;
446             goto done;
447         }
448
449         if (read(fd, &tag, 3) == -1) {
450             r = -4;
451             goto done;
452         }
453
454         if (memcmp(tag, "TAG", 3) == 0) {
455             fprintf(stderr, "id3v1 tag found in file %s\n", finfo->path);
456             if (_parse_id3v1(fd, &info) != 0) {
457                 r = -5;
458                 goto done;
459             }
460         }
461     }
462
463     lms_string_size_strip_and_free(&info.title);
464     lms_string_size_strip_and_free(&info.artist);
465     lms_string_size_strip_and_free(&info.album);
466     lms_string_size_strip_and_free(&info.genre);
467
468     if (!info.title.str) {
469         int ext_idx;
470         ext_idx = ((int)match) - 1;
471         info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
472         info.title.str = malloc((info.title.len + 1) * sizeof(char));
473         memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
474         info.title.str[info.title.len] = '\0';
475     }
476     lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
477
478     if (info.artist.str)
479         lms_charset_conv(ctxt->cs_conv, &info.artist.str, &info.artist.len);
480     if (info.album.str)
481         lms_charset_conv(ctxt->cs_conv, &info.album.str, &info.album.len);
482     if (info.genre.str)
483         lms_charset_conv(ctxt->cs_conv, &info.genre.str, &info.genre.len);
484
485 #if 0
486     fprintf(stderr, "file %s info\n", finfo->path);
487     fprintf(stderr, "\ttitle='%s'\n", info.title.str);
488     fprintf(stderr, "\tartist='%s'\n", info.artist.str);
489     fprintf(stderr, "\talbum='%s'\n", info.album.str);
490     fprintf(stderr, "\tgenre='%s'\n", info.genre.str);
491 #endif
492
493     info.id = finfo->id;
494     r = lms_db_audio_add(plugin->audio_db, &info);
495
496   done:
497     close(fd);
498
499     if (info.title.str)
500         free(info.title.str);
501     if (info.artist.str)
502         free(info.artist.str);
503     if (info.album.str)
504         free(info.album.str);
505     if (info.genre.str)
506         free(info.genre.str);
507
508     return r;
509 }
510
511 static int
512 _setup(struct plugin *plugin, struct lms_context *ctxt)
513 {
514     plugin->audio_db = lms_db_audio_new(ctxt->db);
515     if (!plugin->audio_db)
516         return -1;
517     return 0;
518 }
519
520 static int
521 _start(struct plugin *plugin, struct lms_context *ctxt)
522 {
523     return lms_db_audio_start(plugin->audio_db);
524 }
525
526 static int
527 _finish(struct plugin *plugin, struct lms_context *ctxt)
528 {
529     if (plugin->audio_db)
530         lms_db_audio_free(plugin->audio_db);
531     return 0;
532 }
533
534 static int
535 _close(struct plugin *plugin)
536 {
537     free(plugin);
538     return 0;
539 }
540
541 API struct lms_plugin *
542 lms_plugin_open(void)
543 {
544     struct plugin *plugin;
545
546     plugin = (struct plugin *)malloc(sizeof(*plugin));
547     plugin->plugin.name = _name;
548     plugin->plugin.match = (lms_plugin_match_fn_t)_match;
549     plugin->plugin.parse = (lms_plugin_parse_fn_t)_parse;
550     plugin->plugin.close = (lms_plugin_close_fn_t)_close;
551     plugin->plugin.setup = (lms_plugin_setup_fn_t)_setup;
552     plugin->plugin.start = (lms_plugin_start_fn_t)_start;
553     plugin->plugin.finish = (lms_plugin_finish_fn_t)_finish;
554
555     return (struct lms_plugin *)plugin;
556 }