Rockbox open source high quality audio player as a Music Player Daemon
mpris rockbox mpd libadwaita audio rust zig deno
2
fork

Configure Feed

Select the types of activity you want to include in your feed.

metadata: opus, vorbis, speex: support embedded jpeg album art

It includes .opus, .ogg, .oga, .spx files

Change-Id: I3d0ee9806b05911fc8c3ce5cb761de87d4166141

+297 -146
+65 -10
apps/recorder/jpeg_load.c
··· 26 26 * KIND, either express or implied. 27 27 * 28 28 ****************************************************************************/ 29 - #include "embedded_metadata.h" 29 + #include "metadata_common.h" 30 30 #include "plugin.h" 31 31 #include "debug.h" 32 32 #include "jpeg_load.h" ··· 81 81 int buf_index; 82 82 83 83 int (*read_buf)(struct jpeg* p_jpeg, size_t count); 84 + bool (*skip_bytes_seek)(struct jpeg* p_jpeg); 84 85 void* custom_param; 85 86 #endif 86 87 unsigned long len; ··· 881 882 return read(p_jpeg->fd, p_jpeg->buf, count); 882 883 } 883 884 885 + INLINE void fill_buf(struct jpeg* p_jpeg) 886 + { 887 + p_jpeg->buf_left = p_jpeg->read_buf(p_jpeg, MIN(JPEG_READ_BUF_SIZE, p_jpeg->len)); 888 + p_jpeg->buf_index = 0; 889 + if (p_jpeg->buf_left > 0) 890 + p_jpeg->len -= p_jpeg->buf_left; 891 + } 892 + 884 893 #ifdef HAVE_ALBUMART 885 894 static int read_buf_id3_unsync(struct jpeg* p_jpeg, size_t count) 886 895 { 887 896 count = read(p_jpeg->fd, p_jpeg->buf, count); 888 897 return id3_unsynchronize(p_jpeg->buf, count, (bool*) &p_jpeg->custom_param); 889 898 } 890 - #endif 891 899 892 - INLINE void fill_buf(struct jpeg* p_jpeg) 900 + static int read_buf_vorbis_base64(struct jpeg* p_jpeg, size_t count) 893 901 { 894 - p_jpeg->buf_left = p_jpeg->read_buf(p_jpeg, MIN(JPEG_READ_BUF_SIZE, p_jpeg->len)); 895 - p_jpeg->buf_index = 0; 896 - if (p_jpeg->buf_left > 0) 897 - p_jpeg->len -= p_jpeg->buf_left; 902 + struct ogg_file* ogg = p_jpeg->custom_param; 903 + unsigned char* buf = p_jpeg->buf; 904 + count = ogg_file_read(ogg, buf, count); 905 + if (count == (size_t) -1) 906 + return 0; 907 + 908 + return base64_decode(buf, count, buf); 898 909 } 899 910 911 + /* when pjpeg->read_buf involves additional data processing (like base64 decoding) 912 + * we can't use lseek and have to call pjpeg->read_buf for proper seek */ 913 + static bool skip_bytes_read_buf(struct jpeg* p_jpeg) 914 + { 915 + do 916 + { 917 + int count = -p_jpeg->buf_left; 918 + fill_buf(p_jpeg); 919 + if (p_jpeg->buf_left < 0) 920 + return false; 921 + p_jpeg->buf_left -= count; 922 + p_jpeg->buf_index += count; 923 + } while (p_jpeg->buf_left < 0); 924 + return true; 925 + } 926 + 927 + #endif /* HAVE_ALBUMART */ 928 + 900 929 static unsigned char *jpeg_getc(struct jpeg* p_jpeg) 901 930 { 902 931 if (UNLIKELY(p_jpeg->buf_left < 1)) ··· 907 936 return (p_jpeg->buf_index++) + p_jpeg->buf; 908 937 } 909 938 910 - INLINE bool skip_bytes_seek(struct jpeg* p_jpeg) 939 + static bool skip_bytes_seek(struct jpeg* p_jpeg) 911 940 { 912 941 if (UNLIKELY(lseek(p_jpeg->fd, -p_jpeg->buf_left, SEEK_CUR) < 0)) 913 942 return false; ··· 919 948 { 920 949 p_jpeg->buf_left -= count; 921 950 p_jpeg->buf_index += count; 922 - return p_jpeg->buf_left >= 0 || skip_bytes_seek(p_jpeg); 951 + return p_jpeg->buf_left >= 0 || p_jpeg->skip_bytes_seek(p_jpeg); 923 952 } 924 953 925 954 static void jpeg_putc(struct jpeg* p_jpeg) ··· 2055 2084 p_jpeg->len = filesize(p_jpeg->fd); 2056 2085 2057 2086 p_jpeg->read_buf = read_buf; 2087 + p_jpeg->skip_bytes_seek = skip_bytes_seek; 2058 2088 2059 2089 #ifdef HAVE_ALBUMART 2060 2090 if (flags & AA_FLAG_ID3_UNSYNC) ··· 2062 2092 p_jpeg->read_buf = read_buf_id3_unsync; 2063 2093 p_jpeg->custom_param = false; 2064 2094 } 2095 + else if (flags & AA_FLAG_VORBIS_BASE64) 2096 + { 2097 + struct ogg_file* ogg = alloca(sizeof(*ogg)); 2098 + off_t pic_pos = lseek(fd, 0, SEEK_CUR); 2099 + 2100 + // we need 92 bytes for format probing, reuse some available space 2101 + unsigned char* buf_format = (unsigned char*) p_jpeg->quanttable; 2102 + int type = get_ogg_format_and_move_to_comments(fd, buf_format); 2103 + 2104 + ogg_file_init(ogg, fd, type, 0); 2105 + bool packet_found; 2106 + do 2107 + { 2108 + int seek_from_cur_pos = pic_pos - lseek(fd, 0, SEEK_CUR); 2109 + packet_found = seek_from_cur_pos <= ogg->packet_remaining; 2110 + if (ogg_file_read(ogg, NULL, packet_found ? seek_from_cur_pos : ogg->packet_remaining) < 0) 2111 + return -1; 2112 + } 2113 + while (!packet_found); 2114 + 2115 + p_jpeg->read_buf = read_buf_vorbis_base64; 2116 + p_jpeg->skip_bytes_seek = skip_bytes_read_buf; 2117 + p_jpeg->custom_param = ogg; 2118 + } 2065 2119 #else 2066 2120 (void)flags; 2067 - #endif 2121 + #endif /* HAVE_ALBUMART */ 2122 + 2068 2123 #endif 2069 2124 status = process_markers(p_jpeg); 2070 2125 #ifndef JPEG_FROM_MEM
-23
lib/rbcodec/metadata/embedded_metadata.h
··· 1 - /*************************************************************************** 2 - * __________ __ ___. 3 - * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 - * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 - * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 - * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 - * \/ \/ \/ \/ \/ 8 - * $Id$ 9 - * 10 - * Copyright (C) 2024 Roman Artiukhin 11 - * 12 - * This program is free software; you can redistribute it and/or 13 - * modify it under the terms of the GNU General Public License 14 - * as published by the Free Software Foundation; either version 2 15 - * of the License, or (at your option) any later version. 16 - * 17 - * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 18 - * KIND, either express or implied. 19 - * 20 - ****************************************************************************/ 21 - #include <stdbool.h> 22 - 23 - int id3_unsynchronize(char* tag, int len, bool *ff_found);
+45 -31
lib/rbcodec/metadata/flac.c
··· 30 30 #include "metadata_parsers.h" 31 31 #include "logf.h" 32 32 33 + #ifdef HAVE_ALBUMART 34 + 35 + bool parse_flac_album_art(unsigned char *buf, int bytes_read, enum mp3_aa_type *type, int *picframe_pos) 36 + { 37 + *picframe_pos = 4; /* skip picture type */ 38 + int mime_length, description_length; 39 + 40 + if (bytes_read <= *picframe_pos + 4) /* get_long_be expects 4 chars */ 41 + { 42 + logf("flac picture length invalid!"); 43 + return false; 44 + } 45 + 46 + mime_length = get_long_be(&buf[(*picframe_pos)]); 47 + 48 + char *mime = buf + *picframe_pos + 4; 49 + *picframe_pos += 4 + mime_length; 50 + 51 + if (bytes_read < *picframe_pos) 52 + { 53 + logf("flac picture length invalid!"); 54 + return false; 55 + } 56 + 57 + *type = AA_TYPE_UNKNOWN; 58 + if (memcmp(mime, "image/", 6) == 0) 59 + { 60 + mime += 6; 61 + if (strcmp(mime, "jpeg") == 0 || strcmp(mime, "jpg") == 0){ 62 + *type = AA_TYPE_JPG; 63 + }else if (strcmp(mime, "png") == 0) 64 + *type = AA_TYPE_PNG; 65 + } 66 + 67 + description_length = get_long_be(&buf[(*picframe_pos)]); 68 + 69 + /* 16 = skip picture width,height,color-depth,color-used */ 70 + *picframe_pos += 4 + description_length + 16; 71 + return true; 72 + } 73 + 74 + #endif /* HAVE_ALBUMART */ 75 + 33 76 bool get_flac_metadata(int fd, struct mp3entry* id3) 34 77 { 35 78 /* A simple parser to read vital metadata from a FLAC file - length, ··· 119 162 if(!id3->has_embedded_albumart) /* only use the first PICTURE */ 120 163 { 121 164 unsigned int buf_size = MIN(sizeof(id3->path), i); 122 - int picframe_pos = 4; /* skip picture type */ 123 - int mime_length, description_length; 124 165 125 166 id3->albumart.pos = lseek(fd, 0, SEEK_CUR); 126 167 127 168 int bytes_read = read(fd, buf, buf_size); 128 169 buf[buf_size-1] = '\0'; 129 170 i -= bytes_read; 130 - if (bytes_read <= picframe_pos + 4) /* get_long_be expects 4 chars */ 131 - { 132 - logf("flac picture length invalid!"); 133 - return false; 134 - } 135 171 136 - mime_length = get_long_be(&buf[picframe_pos]); 137 - 138 - char *mime = buf + picframe_pos + 4; 139 - picframe_pos += 4 + mime_length; 140 - 141 - if (bytes_read < picframe_pos) 142 - { 143 - logf("flac picture length invalid!"); 172 + int picframe_pos; 173 + if (!parse_flac_album_art(buf, bytes_read, &id3->albumart.type, &picframe_pos)) 144 174 return false; 145 - } 146 - 147 - id3->albumart.type = AA_TYPE_UNKNOWN; 148 - if (memcmp(mime, "image/", 6) == 0) 149 - { 150 - mime += 6; 151 - if (strcmp(mime, "jpeg") == 0 || strcmp(mime, "jpg") == 0){ 152 - id3->albumart.type = AA_TYPE_JPG; 153 - }else if (strcmp(mime, "png") == 0) 154 - id3->albumart.type = AA_TYPE_PNG; 155 - } 156 - 157 - description_length = get_long_be(&buf[picframe_pos]); 158 - 159 - /* 16 = skip picture width,height,color-depth,color-used */ 160 - picframe_pos += 4 + description_length + 16; 161 175 162 176 /* if we support the format and image length is in the buffer */ 163 177 if(id3->albumart.type != AA_TYPE_UNKNOWN
-1
lib/rbcodec/metadata/id3tags.c
··· 45 45 #include "mp3data.h" 46 46 #include "metadata_common.h" 47 47 #include "metadata_parsers.h" 48 - #include "embedded_metadata.h" 49 48 #include "misc.h" 50 49 51 50 static unsigned long unsync(unsigned long b0,
+1
lib/rbcodec/metadata/metadata.h
··· 207 207 AA_TYPE_JPG, 208 208 209 209 AA_FLAG_ID3_UNSYNC = 1 << (AA_FLAGS_SHIFT + 0), 210 + AA_FLAG_VORBIS_BASE64 = 1 << (AA_FLAGS_SHIFT + 1), 210 211 }; 211 212 212 213 struct mp3_albumart {
+19
lib/rbcodec/metadata/metadata_common.h
··· 39 39 long read_vorbis_tags(int fd, struct mp3entry *id3, 40 40 long tag_remaining); 41 41 42 + struct ogg_file 43 + { 44 + int fd; 45 + bool packet_ended; 46 + long packet_remaining; 47 + }; 48 + 49 + #ifdef HAVE_ALBUMART 50 + int id3_unsynchronize(char* tag, int len, bool *ff_found); 51 + 52 + size_t base64_decode(const char *in, size_t in_len, unsigned char *out); 53 + 54 + bool parse_flac_album_art(unsigned char *buf, int bytes_read, enum mp3_aa_type *type, int *picframe_pos); 55 + 56 + int get_ogg_format_and_move_to_comments(int fd, unsigned char *buf); 57 + bool ogg_file_init(struct ogg_file* file, int fd, int type, int remaining); 58 + ssize_t ogg_file_read(struct ogg_file* file, void* buffer, size_t buffer_size); 59 + #endif 60 + 42 61 int string_option(const char *option, const char *const oplist[], bool ignore_case); 43 62 bool skip_id3v2(int fd, struct mp3entry *id3); 44 63 long read_string(int fd, char* buf, long buf_size, int eos, long size);
+68 -58
lib/rbcodec/metadata/ogg.c
··· 30 30 #include "metadata_parsers.h" 31 31 #include "logf.h" 32 32 33 - /* A simple parser to read vital metadata from an Ogg Vorbis file. 34 - * Can also handle parsing Ogg Speex files for metadata. Returns 35 - * false if metadata needed by the codec couldn't be read. 36 - */ 37 - bool get_ogg_metadata(int fd, struct mp3entry* id3) 33 + //NOTE: buf size must be >= 92 bytes 34 + int get_ogg_format_and_move_to_comments(int fd, unsigned char *buf) 38 35 { 39 - /* An Ogg File is split into pages, each starting with the string 40 - * "OggS". Each page has a timestamp (in PCM samples) referred to as 41 - * the "granule position". 42 - * 43 - * An Ogg Vorbis has the following structure: 44 - * 1) Identification header (containing samplerate, numchannels, etc) 45 - * 2) Comment header - containing the Vorbis Comments 46 - * 3) Setup header - containing codec setup information 47 - * 4) Many audio packets... 48 - * 49 - * An Ogg Speex has the following structure: 50 - * 1) Identification header (containing samplerate, numchannels, etc) 51 - * Described in this page: (http://www.speex.org/manual2/node7.html) 52 - * 2) Comment header - containing the Vorbis Comments 53 - * 3) Many audio packets. 54 - */ 55 - 56 - /* Use the path name of the id3 structure as a temporary buffer. */ 57 - unsigned char* buf = (unsigned char *)id3->path; 58 - long comment_size; 59 - long remaining = 0; 60 - long last_serial = 0; 61 - long serial, r; 62 - int segments, header_size; 63 - int i; 64 - bool eof = false; 65 - 66 36 /* 92 bytes is enough for both Vorbis and Speex headers */ 67 37 if ((lseek(fd, 0, SEEK_SET) < 0) || (read(fd, buf, 92) < 92)) 68 38 { 69 - return false; 39 + return AFMT_UNKNOWN; 70 40 } 71 41 72 42 /* All Ogg streams start with OggS */ 73 43 if (memcmp(buf, "OggS", 4) != 0) 74 44 { 75 - return false; 45 + return AFMT_UNKNOWN; 76 46 } 77 47 78 48 /* Check for format magic and then get metadata */ 79 49 if (memcmp(&buf[29], "vorbis", 6) == 0) 80 50 { 81 - id3->codectype = AFMT_OGG_VORBIS; 82 - id3->frequency = get_long_le(&buf[40]); 83 - id3->vbr = true; 84 - 85 51 /* Comments are in second Ogg page (byte 58 onwards for Vorbis) */ 86 52 if (lseek(fd, 58, SEEK_SET) < 0) 87 53 { 88 - return false; 54 + return AFMT_UNKNOWN; 89 55 } 56 + return AFMT_OGG_VORBIS; 90 57 } 91 58 else if (memcmp(&buf[28], "Speex ", 8) == 0) 92 59 { 93 - id3->codectype = AFMT_SPEEX; 94 - id3->frequency = get_slong(&buf[64]); 95 - id3->vbr = get_long_le(&buf[88]); 96 - 97 - header_size = get_long_le(&buf[60]); 60 + uint32_t header_size = get_long_le(&buf[60]); 98 61 99 62 /* Comments are in second Ogg page (byte 108 onwards for Speex) */ 100 63 if (lseek(fd, 28 + header_size, SEEK_SET) < 0) 101 64 { 102 - return false; 65 + return AFMT_UNKNOWN; 103 66 } 67 + 68 + return AFMT_SPEEX; 104 69 } 105 70 else if (memcmp(&buf[28], "OpusHead", 8) == 0) 106 71 { 107 - id3->codectype = AFMT_OPUS; 108 - id3->frequency = 48000; 109 - id3->vbr = true; 110 - 111 - // FIXME handle an actual channel mapping table 112 72 /* Comments are in second Ogg page (byte 108 onwards for Speex) */ 113 73 if (lseek(fd, 47, SEEK_SET) < 0) 114 74 { 115 - DEBUGF("Couldnotseektoogg"); 116 - return false; 75 + DEBUGF("Could not seek to ogg"); 76 + return AFMT_UNKNOWN; 117 77 } 78 + return AFMT_OPUS; 118 79 } 119 - else 80 + /* Unsupported format, try to print the marker, catches Ogg/FLAC at least */ 81 + DEBUGF("Unsupported format in Ogg stream: %16s\n", &buf[28]); 82 + return AFMT_UNKNOWN; 83 + } 84 + 85 + /* A simple parser to read vital metadata from an Ogg Vorbis file. 86 + * Can also handle parsing Ogg Speex files for metadata. Returns 87 + * false if metadata needed by the codec couldn't be read. 88 + */ 89 + bool get_ogg_metadata(int fd, struct mp3entry* id3) 90 + { 91 + /* An Ogg File is split into pages, each starting with the string 92 + * "OggS". Each page has a timestamp (in PCM samples) referred to as 93 + * the "granule position". 94 + * 95 + * An Ogg Vorbis has the following structure: 96 + * 1) Identification header (containing samplerate, numchannels, etc) 97 + * 2) Comment header - containing the Vorbis Comments 98 + * 3) Setup header - containing codec setup information 99 + * 4) Many audio packets... 100 + * 101 + * An Ogg Speex has the following structure: 102 + * 1) Identification header (containing samplerate, numchannels, etc) 103 + * Described in this page: (http://www.speex.org/manual2/node7.html) 104 + * 2) Comment header - containing the Vorbis Comments 105 + * 3) Many audio packets. 106 + */ 107 + 108 + /* Use the path name of the id3 structure as a temporary buffer. */ 109 + unsigned char* buf = (unsigned char *)id3->path; 110 + long comment_size; 111 + long last_serial = 0; 112 + long serial, r; 113 + int segments; 114 + int i; 115 + bool eof = false; 116 + 117 + id3->codectype = get_ogg_format_and_move_to_comments(fd, buf); 118 + switch (id3->codectype) 120 119 { 121 - /* Unsupported format, try to print the marker, catches Ogg/FLAC at least */ 122 - DEBUGF("Usupported format in Ogg stream: %16s\n", &buf[28]); 123 - return false; 120 + case AFMT_OGG_VORBIS: 121 + id3->frequency = get_long_le(&buf[40]); 122 + id3->vbr = true; 123 + break; 124 + case AFMT_SPEEX: 125 + id3->frequency = get_slong(&buf[64]); 126 + id3->vbr = get_long_le(&buf[88]); 127 + break; 128 + case AFMT_OPUS: 129 + id3->frequency = 48000; 130 + id3->vbr = true; 131 + // FIXME handle an actual channel mapping table 132 + break; 133 + default: 134 + return false; 124 135 } 125 136 126 137 id3->filesize = filesize(fd); ··· 129 140 * one from the last page (since we only support a single bitstream). 130 141 */ 131 142 serial = get_long_le(&buf[14]); 143 + long remaining = 0; 132 144 comment_size = read_vorbis_tags(fd, id3, remaining); 133 145 134 146 /* We now need to search for the last page in the file - identified by ··· 140 152 { 141 153 return false; 142 154 } 143 - 144 - remaining = 0; 145 155 146 156 while (!eof) 147 157 {
+99 -23
lib/rbcodec/metadata/vorbis.c
··· 26 26 #include "platform.h" 27 27 #include "metadata.h" 28 28 #include "metadata_common.h" 29 - #include "metadata_parsers.h" 30 29 31 30 /* Define LOGF_ENABLE to enable logf output in this file */ 32 31 /*#define LOGF_ENABLE*/ 33 32 #include "logf.h" 34 33 35 - struct file 36 - { 37 - int fd; 38 - bool packet_ended; 39 - long packet_remaining; 40 - }; 41 - 42 - 43 34 /* Read an Ogg page header. file->packet_remaining is set to the size of the 44 35 * first packet on the page; file->packet_ended is set to true if the packet 45 36 * ended on the current page. Returns true if the page header was 46 37 * successfully read. 47 38 */ 48 - static bool file_read_page_header(struct file* file) 39 + static bool file_read_page_header(struct ogg_file* file) 49 40 { 50 41 unsigned char buffer[64]; 51 42 ssize_t table_left; ··· 110 101 * 0 if there is no more data to read (in the packet or the file), < 0 if a 111 102 * read error occurred. 112 103 */ 113 - static ssize_t file_read(struct file* file, void* buffer, size_t buffer_size) 104 + ssize_t ogg_file_read(struct ogg_file* file, void* buffer, size_t buffer_size) 114 105 { 115 106 ssize_t done = 0; 116 107 ssize_t count = -1; ··· 167 158 168 159 /* Read an int32 from file. Returns false if a read error occurred. 169 160 */ 170 - static bool file_read_int32(struct file* file, int32_t* value) 161 + static bool file_read_int32(struct ogg_file* file, int32_t* value) 171 162 { 172 163 char buf[sizeof(int32_t)]; 173 164 174 - if (file_read(file, buf, sizeof(buf)) < (ssize_t) sizeof(buf)) 165 + if (ogg_file_read(file, buf, sizeof(buf)) < (ssize_t) sizeof(buf)) 175 166 { 176 167 return false; 177 168 } ··· 190 181 * Unfortunately this is a slightly modified copy of read_string() in 191 182 * metadata_common.c... 192 183 */ 193 - static long file_read_string(struct file* file, char* buffer, 184 + static long file_read_string(struct ogg_file* file, char* buffer, 194 185 long buffer_size, int eos, long size) 195 186 { 196 187 long read_bytes = 0; ··· 199 190 { 200 191 char c; 201 192 202 - if (file_read(file, &c, 1) != 1) 193 + if (ogg_file_read(file, &c, 1) != 1) 203 194 { 204 195 read_bytes = -1; 205 196 break; ··· 221 212 else if (eos == -1) 222 213 { 223 214 /* No point in reading any more, skip remaining data */ 224 - if (file_read(file, NULL, size) < 0) 215 + if (ogg_file_read(file, NULL, size) < 0) 225 216 { 226 217 read_bytes = -1; 227 218 } ··· 244 235 * max amount to read if codec type is FLAC; it is ignored otherwise. 245 236 * Returns true if the file was successfully initialized. 246 237 */ 247 - static bool file_init(struct file* file, int fd, int type, int remaining) 238 + bool ogg_file_init(struct ogg_file* file, int fd, int type, int remaining) 248 239 { 249 240 memset(file, 0, sizeof(*file)); 250 241 file->fd = fd; ··· 262 253 char buffer[7]; 263 254 264 255 /* Read packet header (type and id string) */ 265 - if (file_read(file, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) 256 + if (ogg_file_read(file, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) 266 257 { 267 258 return false; 268 259 } ··· 280 271 char buffer[8]; 281 272 282 273 /* Read comment header */ 283 - if (file_read(file, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) 274 + if (ogg_file_read(file, buffer, sizeof(buffer)) < (ssize_t) sizeof(buffer)) 284 275 { 285 276 return false; 286 277 } ··· 300 291 return true; 301 292 } 302 293 294 + #define B64_START_CHAR '+' 295 + /* maps char codes to BASE64 codes ('A': 0, 'B': 1,... '+': 62, '-': 63 */ 296 + const char b64_codes[] = 297 + { /* Starts from first valid base 64 char '+' with char code 43 (B64_START_CHAR) 298 + * For valid base64 chars: index in 0..63; for invalid: -1, for =: -2 */ 299 + 62, -1, -1, -1, 63, /* 43-47 (+ /) */ 300 + 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -2, -1, -1, /* 48-63 (0-9 and =) */ 301 + -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, /* 64-79 (A-O) */ 302 + 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1, /* 80-95 (P-Z) */ 303 + -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, /* 96-111 (a-o) */ 304 + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51 /* 112-127 (p-z) */ 305 + }; 306 + 307 + size_t base64_decode(const char *in, size_t in_len, unsigned char *out) 308 + { 309 + size_t i = 0; 310 + int val = 0; 311 + size_t len = 0; 312 + 313 + while (i < in_len) 314 + { 315 + if (in[i] == '=') //is it padding? 316 + { 317 + switch (i & 3) 318 + { 319 + case 2: 320 + out[len++] = (val >> 4) & 0xFF; 321 + break; 322 + case 3: 323 + out[len++] = (val >> 10) & 0xFF; 324 + out[len++] = (val >> 2) & 0xFF; 325 + break; 326 + } 327 + break; 328 + } 329 + 330 + val = (val << 6) | b64_codes[in[i] - B64_START_CHAR]; 331 + 332 + if ((++i & 3) == 0) 333 + { 334 + out[len++] = (val >> 16) & 0xFF; 335 + out[len++] = (val >> 8) & 0xFF; 336 + out[len++] = val & 0xFF; 337 + } 338 + } 339 + return len; 340 + } 341 + 342 + size_t base64_encoded_size(size_t inlen) 343 + { 344 + size_t ret = inlen; 345 + if (inlen % 3 != 0) 346 + ret += 3 - (inlen % 3); 347 + ret /= 3; 348 + ret *= 4; 349 + 350 + return ret; 351 + } 303 352 304 353 /* Read the items in a Vorbis comment packet. For Ogg files, the file must 305 354 * be located on a page start, for other files, the beginning of the comment ··· 309 358 long read_vorbis_tags(int fd, struct mp3entry *id3, 310 359 long tag_remaining) 311 360 { 312 - struct file file; 361 + struct ogg_file file; 313 362 char *buf = id3->id3v2buf; 314 363 int32_t comment_count; 315 364 int32_t len; ··· 317 366 int buf_remaining = sizeof(id3->id3v2buf) + sizeof(id3->id3v1buf); 318 367 int i; 319 368 320 - if (!file_init(&file, fd, id3->codectype, tag_remaining)) 369 + if (!ogg_file_init(&file, fd, id3->codectype, tag_remaining)) 321 370 { 322 371 return 0; 323 372 } 324 373 325 374 /* Skip vendor string */ 326 375 327 - if (!file_read_int32(&file, &len) || (file_read(&file, NULL, len) < 0)) 376 + if (!file_read_int32(&file, &len) || (ogg_file_read(&file, NULL, len) < 0)) 328 377 { 329 378 return 0; 330 379 } ··· 355 404 } 356 405 357 406 len -= read_len; 407 + #ifdef HAVE_ALBUMART 408 + int before_block_pos = lseek(fd, 0, SEEK_CUR); 409 + #endif 358 410 read_len = file_read_string(&file, id3->path, sizeof(id3->path), -1, len); 359 411 360 412 if (read_len < 0) ··· 363 415 } 364 416 365 417 logf("Vorbis comment %d: %s=%s", i, name, id3->path); 418 + #ifdef HAVE_ALBUMART 419 + if (!id3->has_embedded_albumart /* only use the first PICTURE */ 420 + && !strcasecmp(name, "METADATA_BLOCK_PICTURE")) 421 + { 422 + int after_block_pos = lseek(fd, 0, SEEK_CUR); 366 423 424 + char* buf = id3->path; 425 + size_t outlen = base64_decode(buf, MIN(read_len, (int32_t) sizeof(id3->path)), buf); 426 + 427 + int picframe_pos; 428 + parse_flac_album_art(buf, outlen, &id3->albumart.type, &picframe_pos); 429 + if(id3->albumart.type != AA_TYPE_UNKNOWN) 430 + { 431 + //NOTE: This is not exact location due to padding in base64 (up to 3 chars)!! 432 + // But it's OK with our jpeg decoder if we add or miss few bytes in jpeg header 433 + const int picframe_pos_b64 = base64_encoded_size(picframe_pos + 4); 434 + 435 + id3->has_embedded_albumart = true; 436 + id3->albumart.type |= AA_FLAG_VORBIS_BASE64; 437 + id3->albumart.pos = picframe_pos_b64 + before_block_pos; 438 + id3->albumart.size = after_block_pos - id3->albumart.pos; 439 + } 440 + } 441 + else 442 + #endif 367 443 /* Is it an embedded cuesheet? */ 368 444 if (!strcasecmp(name, "CUESHEET")) 369 445 { ··· 385 461 /* Skip to the end of the block (needed by FLAC) */ 386 462 if (file.packet_remaining) 387 463 { 388 - if (file_read(&file, NULL, file.packet_remaining) < 0) 464 + if (ogg_file_read(&file, NULL, file.packet_remaining) < 0) 389 465 { 390 466 return 0; 391 467 }