Auxiliary functions to parse and validate parts of URIs.
[paraslash.git] / aac_afh.c
index 11dde5711976daad4ca42da1d6c8ca895e7a3972..80b50a4d55b6645aba8331c562b3667dc42663f7 100644 (file)
--- a/aac_afh.c
+++ b/aac_afh.c
@@ -42,6 +42,135 @@ static int aac_find_stsz(unsigned char *buf, size_t buflen, off_t *skip)
        return -E_STSZ;
 }
 
+static int atom_cmp(unsigned char *buf1, char *buf2)
+{
+       unsigned char *b2 = (unsigned char *)buf2;
+
+       if (buf1[0] != b2[0])
+               return 1;
+       if (buf1[1] != b2[1])
+               return 1;
+       if (buf1[2] != b2[2])
+               return 1;
+       if (buf1[3] != b2[3])
+               return 1;
+       return 0;
+}
+
+static int read_atom_header(unsigned char *buf, uint64_t *subsize, unsigned char type[5])
+{
+       int i;
+       uint64_t size = (buf[0] << 24) + (buf[1] << 16) + (buf[2] << 8) + buf[3];
+
+       memcpy(type, buf + 4, 4);
+       type[4] = '\0';
+
+       PARA_DEBUG_LOG("size: %llu, type: %s\n", (long long unsigned)size, type);
+       if (size != 1) {
+               *subsize = size;
+               return 8;
+       }
+       buf += 4;
+       size = 0;
+       for (i = 0; i < 8; i++)
+               size |= ((uint64_t)buf[i]) << ((7 - i) * 8);
+       *subsize = size;
+       return 16;
+}
+
+static char *get_tag(unsigned char *p, int size)
+{
+       char *buf;
+
+       assert(size > 0);
+       buf = para_malloc(size + 1);
+
+       memcpy(buf, p, size);
+       buf[size] = '\0';
+       PARA_DEBUG_LOG("size: %d: %s\n", size, buf);
+       return buf;
+}
+
+static char *read_tags(unsigned char *buf, size_t buflen)
+{
+       unsigned char *p = buf;
+       char *title = NULL, *artist = NULL, *album = NULL, *year = NULL,
+               *comment = NULL, *result;
+
+       while (p + 32 < buf + buflen) {
+               unsigned char *q, type1[5], type2[5];
+               uint64_t size1, size2;
+               int ret, ret2;
+
+               ret = read_atom_header(p, &size1, type1);
+               ret2 = read_atom_header(p + ret, &size2, type2);
+
+               if (size2 <= 16 || atom_cmp(type2, "data")) {
+                       p += size1;
+                       continue;
+               }
+               size2 -= 16;
+               q = p + ret + ret2 + 8;
+               if (q + size2 > buf + buflen)
+                       break;
+               if (!atom_cmp(type1, "©ART"))
+                       artist = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©alb"))
+                       album = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©nam"))
+                       title = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©cmt"))
+                       comment = get_tag(q, size2);
+               else if (!atom_cmp(type1, "©day"))
+                       year = get_tag(q, size2);
+               p += size1;
+       }
+       result = make_taginfo(title, artist, album, year, comment);
+       free(title);
+       free(artist);
+       free(album);
+       free(year);
+       free(comment);
+       return result;
+}
+
+static char *read_meta(unsigned char *buf, size_t buflen)
+{
+       unsigned char *p = buf;
+
+       while (p + 4 < buf + buflen) {
+
+               if (p[0] != 'i' || p[1] != 'l' || p[2] != 's' || p[3] != 't') {
+                       p++;
+                       continue;
+               }
+               p += 4;
+               return read_tags(p, buflen - (p - buf));
+       }
+       return make_taginfo(NULL, NULL, NULL, NULL, NULL);
+}
+
+static char *aac_get_taginfo(unsigned char *buf, size_t buflen)
+{
+       int i;
+       uint64_t subsize;
+       unsigned char type[5];
+
+       for (i = 0; i + 24 < buflen; i++) {
+               unsigned char *p = buf + i;
+               if (p[0] != 'm' || p[1] != 'e' || p[2] != 't' || p[3] != 'a')
+                       continue;
+               PARA_INFO_LOG("found metadata at offset %d\n", i);
+               i += 8;
+               p = buf + i;
+               i += read_atom_header(p, &subsize, type);
+               p = buf + i;
+               return read_meta(p, buflen - i);
+       }
+       PARA_INFO_LOG("no meta data\n");
+       return make_taginfo(NULL, NULL, NULL, NULL, NULL);
+}
+
 static ssize_t aac_compute_chunk_table(struct afh_info *afhi,
                unsigned char *map, size_t numbytes)
 {
@@ -92,7 +221,7 @@ static int aac_set_chunk_tv(struct afh_info *afhi,
 /*
  * Init m4a file and write some tech data to given pointers.
  */
-static int aac_get_file_info(char *map, size_t numbytes,
+static int aac_get_file_info(char *map, size_t numbytes, __a_unused int fd,
                struct afh_info *afhi)
 {
        int i;
@@ -103,10 +232,12 @@ static int aac_get_file_info(char *map, size_t numbytes,
        mp4AudioSpecificConfig mp4ASC;
        NeAACDecHandle handle = NULL;
        unsigned char *umap = (unsigned char *) map;
+       char *taginfo;
 
        ret = aac_find_esds(umap, numbytes, &skip, &decoder_len);
        if (ret < 0)
                goto out;
+       taginfo = aac_get_taginfo(umap, numbytes);
        handle = aac_open();
        ret = -E_AAC_AFH_INIT;
        if (NeAACDecInit(handle, umap + skip, decoder_len, &rate, &channels))
@@ -137,11 +268,10 @@ static int aac_get_file_info(char *map, size_t numbytes,
        ret = (afhi->chunk_table[afhi->chunks_total] - afhi->chunk_table[0]) * 8; /* bits */
        ret += (channels * afhi->seconds_total * 500); /* avoid rounding error */
        afhi->bitrate = ret / (channels * afhi->seconds_total * 1000);
-       sprintf(afhi->info_string, "%s:\n%s:\n%s:\n",
+       afhi->info_string = make_message("%s:\n%s",
                status_item_list[SI_AUDIO_FILE_INFO],
-               status_item_list[SI_TAGINFO1],
-               status_item_list[SI_TAGINFO2]
-       );
+               taginfo);
+       free(taginfo);
        tv_scale(20, &afhi->chunk_tv, &afhi->eof_tv);
        ret = 1;
 out: