diff --git a/apps/buffering.c b/apps/buffering.c index 46b6455..b595de1 100644 --- a/apps/buffering.c +++ b/apps/buffering.c @@ -52,6 +52,7 @@ #include "albumart.h" #include "jpeg_load.h" #include "bmp.h" +#include "playback.h" #endif #define GUARD_BUFSIZE (32*1024) @@ -832,7 +833,7 @@ static void shrink_handle(struct memory_handle *h) if (h->next && h->filerem == 0 && (h->type == TYPE_ID3 || h->type == TYPE_CUESHEET || h->type == TYPE_BITMAP || h->type == TYPE_CODEC || - h->type == TYPE_ATOMIC_AUDIO)) + h->type == TYPE_ATOMIC_AUDIO || h->type == TYPE_BUFFER)) { /* metadata handle: we can move all of it */ uintptr_t handle_distance = @@ -908,10 +909,12 @@ static bool fill_buffer(void) /* Given a file descriptor to a bitmap file, write the bitmap data to the buffer, with a struct bitmap and the actual data immediately following. Return value is the total size (struct + data). */ -static int load_image(int fd, const char *path, struct dim *dim) +static int load_image(int fd, const char *path, struct bufopen_data *data) { int rc; struct bitmap *bmp = (struct bitmap *)&buffer[buf_widx]; + struct dim *dim = data->dim; + struct mp3_albumart *aa = data->albumart; /* get the desired image size */ bmp->width = dim->width, bmp->height = dim->height; @@ -928,8 +931,15 @@ static int load_image(int fd, const char *path, struct dim *dim) - sizeof(struct bitmap); #ifdef HAVE_JPEG - int pathlen = strlen(path); - if (strcmp(path + pathlen - 4, ".bmp")) + if (aa != NULL && aa->embed_albumart) + { + /* FIXME: aa.pos is parsed incorrectly, my test file needs an additonal + * 0x1b (probably alignment, (aa->pos+0x7f) & ~0x7f works as well) */ + lseek(fd, aa->pos + 0x1b, SEEK_SET); + rc = clip_jpeg_fd(fd, aa->size, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| + FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); + } + else if (strcmp(path + strlen(path) - 4, ".bmp")) rc = read_jpeg_fd(fd, bmp, free, FORMAT_NATIVE|FORMAT_DITHER| FORMAT_RESIZE|FORMAT_KEEP_ASPECT, NULL); else @@ -1058,7 +1068,7 @@ int bufopen(const char *file, size_t offset, enum data_type type, /* Bitmap file: we load the data instead of the file */ int rc; mutex_lock(&llist_mod_mutex); /* Lock because load_bitmap yields */ - rc = load_image(fd, file, (struct dim*)user_data); + rc = load_image(fd, file, (struct bufopen_data*)user_data); mutex_unlock(&llist_mod_mutex); if (rc <= 0) { diff --git a/apps/metadata.h b/apps/metadata.h index 8c7188d..046f992 100644 --- a/apps/metadata.h +++ b/apps/metadata.h @@ -186,6 +186,21 @@ enum { ID3_VER_2_4 }; +#ifdef HAVE_ALBUMART +enum mp3_aa_type { + AA_TYPE_UNKNOWN, + AA_TYPE_BMP, + AA_TYPE_PNG, + AA_TYPE_JPG, +}; + +struct mp3_albumart { + enum mp3_aa_type type; + int size; + off_t pos; +}; +#endif + struct mp3entry { char path[MAX_PATH]; char* title; @@ -267,6 +282,11 @@ struct mp3entry { long album_peak; #endif +#ifdef HAVE_ALBUMART + bool embed_albumart; + struct mp3_albumart albumart; +#endif + /* Cuesheet support */ struct cuesheet *cuesheet; diff --git a/apps/metadata/id3tags.c b/apps/metadata/id3tags.c index f3ddd73..14c84b9 100644 --- a/apps/metadata/id3tags.c +++ b/apps/metadata/id3tags.c @@ -290,6 +290,41 @@ static int parsegenre( struct mp3entry* entry, char* tag, int bufferpos ) } } +#ifdef HAVE_ALBUMART +/* parse embed albumart */ +static int parsealbumart( struct mp3entry* entry, char* tag, int bufferpos ) +{ + entry->albumart.type = AA_TYPE_UNKNOWN; + + if (memcmp(++tag, "image/", 6) == 0) + { + /* ID3 v2.3+ */ + tag += 6; + if (memcmp(tag, "jpeg", 4) == 0) + entry->albumart.type = AA_TYPE_JPG; + else if (memcmp(tag, "png", 3) == 0) + entry->albumart.type = AA_TYPE_PNG; + + entry->albumart.pos += 14; + entry->albumart.size -= 14; + } + else + { + /* ID3 v2.2 */ + if (memcmp(tag, "JPG", 3) == 0) + entry->albumart.type = AA_TYPE_JPG; + else if (memcmp(tag, "PNG", 3) == 0) + entry->albumart.type = AA_TYPE_PNG; + + entry->albumart.pos += 6; + entry->albumart.size -= 6; + } + + entry->embed_albumart = (entry->albumart.type != AA_TYPE_UNKNOWN); + return bufferpos; +} +#endif + /* parse user defined text, looking for album artist and replaygain * information. */ @@ -439,6 +474,10 @@ static const struct tag_resolver taglist[] = { { "COM", 3, offsetof(struct mp3entry, comment), NULL, false }, { "TCON", 4, offsetof(struct mp3entry, genre_string), &parsegenre, false }, { "TCO", 3, offsetof(struct mp3entry, genre_string), &parsegenre, false }, +#ifdef HAVE_ALBUMART + { "APIC", 4, 0, &parsealbumart, true }, + { "PIC", 3, 0, &parsealbumart, true }, +#endif { "TXXX", 4, 0, &parseuser, false }, #if CONFIG_CODEC == SWCODEC { "RVA2", 4, 0, &parserva2, true }, @@ -955,6 +994,15 @@ void setid3v2title(int fd, struct mp3entry *entry) if (ptag && !*ptag) *ptag = tag; + /* albumart */ + if ((!entry->embed_albumart) && + ((tr->tag_length == 4 && !memcmp( header, "APIC", 4)) || + (tr->tag_length == 3 && !memcmp( header, "PIC" , 3)))) + { + entry->albumart.pos = lseek(fd, 0, SEEK_CUR) - framelen; + entry->albumart.size = totframelen; + } + if( tr->ppFunc ) bufferpos = tr->ppFunc(entry, tag, bufferpos); diff --git a/apps/playback.c b/apps/playback.c index e71e06b..896e5d7 100644 --- a/apps/playback.c +++ b/apps/playback.c @@ -42,6 +42,7 @@ #ifdef HAVE_LCD_BITMAP #ifdef HAVE_ALBUMART #include "albumart.h" +#include "recorder/jpeg_load.h" #endif #endif #include "sound.h" @@ -142,6 +143,7 @@ static struct cuesheet *curr_cue = NULL; #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT #ifdef HAVE_ALBUMART + static struct albumart_slot { struct dim dim; /* holds width, height of the albumart */ int used; /* counter, increments if something uses it */ @@ -228,7 +230,6 @@ static bool audio_have_tracks(void); static void audio_reset_buffer(void); static void audio_stop_playback(void); - /**************************************/ @@ -647,6 +648,7 @@ bool audio_peek_track(struct mp3entry** id3, int offset) } #ifdef HAVE_ALBUMART + int playback_current_aa_hid(int slot) { if (slot < 0) @@ -656,13 +658,13 @@ int playback_current_aa_hid(int slot) cur_idx = track_ridx + offset; cur_idx &= MAX_TRACK_MASK; - return tracks[cur_idx].aa_hid[slot]; } int playback_claim_aa_slot(struct dim *dim) { int i; + /* first try to find a slot already having the size to reuse it * since we don't want albumart of the same size buffered multiple times */ FOREACH_ALBUMART(i) @@ -691,8 +693,12 @@ int playback_claim_aa_slot(struct dim *dim) void playback_release_aa_slot(int slot) { + if (slot < 0) + return; + /* invalidate the albumart_slot */ struct albumart_slot *aa_slot = &albumart_slots[slot]; + if (aa_slot->used > 0) aa_slot->used--; } @@ -1310,6 +1316,7 @@ static void audio_finish_load_track(void) { int i; char aa_path[MAX_PATH]; + FOREACH_ALBUMART(i) { /* albumart_slots may change during a yield of bufopen, @@ -1317,16 +1324,30 @@ static void audio_finish_load_track(void) if (tracks[track_widx].aa_hid[i] >= 0 || !albumart_slots[i].used) continue; /* find_albumart will error out if the wps doesn't have AA */ - if (find_albumart(track_id3, aa_path, sizeof(aa_path), + if ((track_id3->embed_albumart && track_id3->albumart.type == AA_TYPE_JPG) + || find_albumart(track_id3, aa_path, sizeof(aa_path), &(albumart_slots[i].dim))) { - int aa_hid = bufopen(aa_path, 0, TYPE_BITMAP, - &(albumart_slots[i].dim)); - + int aa_hid; + struct bufopen_data user_data = { + .dim = &(albumart_slots[i].dim), + .albumart = NULL, + }; + if (track_id3->embed_albumart) + { + user_data.albumart = &(track_id3->albumart); + aa_hid = bufopen(track_id3->path, 0, + TYPE_BITMAP, &user_data); + } + else + { + aa_hid = bufopen(aa_path, 0, TYPE_BITMAP, + &user_data); + } if(aa_hid == ERR_BUFFER_FULL) { filling = STATE_FULL; - logf("buffer is full for now (get album art)"); + logf("buffer is full for now (get album art)\n"); return; /* No space for track's album art, not an error */ } else if (aa_hid < 0) @@ -1337,7 +1358,6 @@ static void audio_finish_load_track(void) tracks[track_widx].aa_hid[i] = aa_hid; } } - } #endif @@ -1479,7 +1499,9 @@ static void audio_fill_file_buffer(bool start_play, size_t offset) file size shouldn't have changed so we can go straight from AUDIOBUF_STATE_VOICED_ONLY to AUDIOBUF_STATE_INITIALIZED */ if (buffer_state != AUDIOBUF_STATE_INITIALIZED) + { audio_reset_buffer(); + } logf("Starting buffer fill"); @@ -2059,6 +2081,10 @@ static void audio_thread(void) /* release tracks to make sure all handles are closed */ audio_release_tracks(); + +#ifdef HAVE_ALBUMART + close_embed_albumart(); +#endif break; #endif diff --git a/apps/playback.h b/apps/playback.h index 27e27ff..ddf59c3 100644 --- a/apps/playback.h +++ b/apps/playback.h @@ -29,6 +29,7 @@ #ifdef HAVE_ALBUMART #include "bmp.h" +#include "metadata.h" /* * Returns the handle id of the buffered albumart for the given slot id **/ @@ -50,6 +51,12 @@ int playback_claim_aa_slot(struct dim *dim); * * Save to call from other threads */ void playback_release_aa_slot(int slot); + +struct bufopen_data { + struct dim *dim; + struct mp3_albumart *albumart; +}; + #endif /* Functions */ diff --git a/apps/plugin.c b/apps/plugin.c index 690aee9..a451e16 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -710,8 +710,8 @@ static const struct plugin_api rockbox_api = { read_bmp_file, read_bmp_fd, #ifdef HAVE_JPEG - read_jpeg_file, - read_jpeg_fd, + clip_jpeg_file, + clip_jpeg_fd, #endif screen_dump_set_hook, #endif diff --git a/apps/plugin.h b/apps/plugin.h index 4b11ac3..dd52d2e 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -834,9 +834,10 @@ struct plugin_api { int (*read_bmp_fd)(int fd, struct bitmap *bm, int maxsize, int format, const struct custom_format *cformat); #ifdef HAVE_JPEG - int (*read_jpeg_file)(const char* filename, struct bitmap *bm, int maxsize, - int format, const struct custom_format *cformat); - int (*read_jpeg_fd)(int fd, struct bitmap *bm, int maxsize, + int (*clip_jpeg_file)(const char* filename, int offset, unsigned long jpeg_size, + struct bitmap *bm, int maxsize, int format, + const struct custom_format *cformat); + int (*clip_jpeg_fd)(int fd, unsigned long jpeg_size, struct bitmap *bm, int maxsize, int format, const struct custom_format *cformat); #endif void (*screen_dump_set_hook)(void (*hook)(int fh)); diff --git a/apps/plugins/lib/feature_wrappers.h b/apps/plugins/lib/feature_wrappers.h index 50552fb..51dcd0b 100644 --- a/apps/plugins/lib/feature_wrappers.h +++ b/apps/plugins/lib/feature_wrappers.h @@ -47,8 +47,10 @@ #endif #ifdef HAVE_JPEG -#define read_jpeg_file rb->read_jpeg_file -#define read_jpeg_fd rb->read_jpeg_fd +#undef read_jpeg_file +#undef read_jpeg_fd +#define read_jpeg_file(fn, bm, ms, f, cf) rb->clip_jpeg_file(fn, 0, 0, bm, ms, f, cf) +#define read_jpeg_fd(fd, bm, ms, f, cf) rb->clip_jpeg_fd(fd, 0, bm, ms, f, cf) #else #endif diff --git a/apps/recorder/jpeg_load.c b/apps/recorder/jpeg_load.c index 1af65fa..6af5af6 100644 --- a/apps/recorder/jpeg_load.c +++ b/apps/recorder/jpeg_load.c @@ -75,12 +75,12 @@ struct jpeg { #ifdef JPEG_FROM_MEM unsigned char *data; - unsigned long len; #else int fd; int buf_left; int buf_index; #endif + unsigned long len; unsigned long int bitbuf; int bitbuf_bits; int marker_ind; @@ -888,8 +888,12 @@ INLINE void jpeg_putc(struct jpeg* p_jpeg) #else INLINE void fill_buf(struct jpeg* p_jpeg) { - p_jpeg->buf_left = read(p_jpeg->fd, p_jpeg->buf, JPEG_READ_BUF_SIZE); + p_jpeg->buf_left = read(p_jpeg->fd, p_jpeg->buf, + (p_jpeg->len >= JPEG_READ_BUF_SIZE)? + JPEG_READ_BUF_SIZE : p_jpeg->len); p_jpeg->buf_index = 0; + if (p_jpeg->buf_left > 0) + p_jpeg->len -= p_jpeg->buf_left; } static unsigned char *jpeg_getc(struct jpeg* p_jpeg) @@ -1960,7 +1964,9 @@ block_end: * *****************************************************************************/ #ifndef JPEG_FROM_MEM -int read_jpeg_file(const char* filename, +int clip_jpeg_file(const char* filename, + int offset, + unsigned long jpeg_size, struct bitmap *bm, int maxsize, int format, @@ -1975,8 +1981,8 @@ int read_jpeg_file(const char* filename, DEBUGF("read_jpeg_file: can't open '%s', rc: %d\n", filename, fd); return fd * 10 - 1; } - - ret = read_jpeg_fd(fd, bm, maxsize, format, cformat); + lseek(fd, offset, SEEK_SET); + ret = clip_jpeg_fd(fd, jpeg_size, bm, maxsize, format, cformat); close(fd); return ret; } @@ -2014,10 +2020,11 @@ int get_jpeg_dim_mem(unsigned char *data, unsigned long len, return 0; } -int decode_jpeg_mem(unsigned char *data, unsigned long len, +int decode_jpeg_mem(unsigned char *data, #else -int read_jpeg_fd(int fd, +int clip_jpeg_fd(int fd, #endif + unsigned long len, struct bitmap *bm, int maxsize, int format, @@ -2039,11 +2046,13 @@ int read_jpeg_fd(int fd, return -1; #endif memset(p_jpeg, 0, sizeof(struct jpeg)); + p_jpeg->len = len; #ifdef JPEG_FROM_MEM p_jpeg->data = data; - p_jpeg->len = len; #else p_jpeg->fd = fd; + if (p_jpeg->len == 0) + p_jpeg->len = filesize(p_jpeg->fd); #endif status = process_markers(p_jpeg); #ifndef JPEG_FROM_MEM diff --git a/apps/recorder/jpeg_load.h b/apps/recorder/jpeg_load.h index 73b6c51..b783250 100644 --- a/apps/recorder/jpeg_load.h +++ b/apps/recorder/jpeg_load.h @@ -32,13 +32,20 @@ #ifndef _JPEG_LOAD_H #define _JPEG_LOAD_H -int read_jpeg_file(const char* filename, +#define read_jpeg_file(fn, bm, ms, f, cf) clip_jpeg_file(fn, 0, 0, bm, ms, f, cf) + +int clip_jpeg_file(const char* filename, + int offset, + unsigned long jpeg_size, struct bitmap *bm, int maxsize, int format, const struct custom_format *cformat); -int read_jpeg_fd(int fd, +#define read_jpeg_fd(fd, bm, ms, f, cf) clip_jpeg_fd(fd, 0, bm, ms, f, cf) + +int clip_jpeg_fd(int fd, + unsigned long jpeg_size, struct bitmap *bm, int maxsize, int format,