diff --git a/apps/buffering.c b/apps/buffering.c index 8a6418f..deecbce 100644 --- a/apps/buffering.c +++ b/apps/buffering.c @@ -833,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 = diff --git a/apps/metadata.h b/apps/metadata.h index 6b15f6d..db4f598 100644 --- a/apps/metadata.h +++ b/apps/metadata.h @@ -184,6 +184,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; @@ -263,6 +278,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 a32d015..5b518e3 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. */ @@ -437,6 +472,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 }, @@ -956,6 +995,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 0be45b0..81f47d7 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" @@ -141,6 +142,19 @@ static struct cuesheet *curr_cue = NULL; #define MAX_MULTIPLE_AA SKINNABLE_SCREENS_COUNT #ifdef HAVE_ALBUMART +#define ALBUMART_SIZE 0x10000 + +static struct embed_albumart { + /* The ID for the embed albumart's handle */ + int hid; + + /* The dimension for the embed albumart */ + struct dim dim; + + /* whether can use */ + bool usable; +} embed_aa; + static struct albumart_slot { struct dim dim; /* holds width, height of the albumart */ int used; /* counter, increments if something uses it */ @@ -227,6 +241,9 @@ static bool audio_have_tracks(void); static void audio_reset_buffer(void); static void audio_stop_playback(void); +#ifdef HAVE_ALBUMART +static void close_embed_albumart(void); +#endif /**************************************/ @@ -368,6 +385,9 @@ void audio_hard_stop(void) #ifdef PLAYBACK_VOICE voice_stop(); #endif +#ifdef HAVE_ALBUMART + close_embed_albumart(); +#endif } bool audio_restore_playback(int type) @@ -648,22 +668,90 @@ bool audio_peek_track(struct mp3entry** id3, int offset) } #ifdef HAVE_ALBUMART + +/* --- embed albumart --- */ +static void open_embed_albumart(void) +{ + if (embed_aa.hid < 0) + { + /* allocate size is bitmap size + working buffer size */ + embed_aa.hid = bufalloc(NULL, ALBUMART_SIZE, TYPE_BITMAP); + } +} + +static void close_embed_albumart(void) +{ + if (embed_aa.hid >= 0) + { + bufclose(embed_aa.hid); + embed_aa.hid = -1; + } + embed_aa.usable = false; +} + +static bool regist_embed_albumart(const struct mp3entry *id3) +{ + struct bitmap *bmp; + + embed_aa.usable = false; + if (!id3 || !id3->embed_albumart || embed_aa.hid < 0) + return false; + + if (bufgetdata(embed_aa.hid, 0, (void**)&bmp) < 0) + { + logf("bitmap handle does not get from the buffer"); + return false; + } + + if (id3->albumart.type == AA_TYPE_JPG) + { + bmp->width = embed_aa.dim.width; + bmp->height = embed_aa.dim.height; + bmp->data = (unsigned char *)bmp + sizeof(struct bitmap); + + if (clip_jpeg_file(id3->path, id3->albumart.pos, id3->albumart.size, + bmp, ALBUMART_SIZE, + FORMAT_NATIVE|FORMAT_DITHER|FORMAT_KEEP_ASPECT|FORMAT_RESIZE, + NULL) < 0) + { + logf("bitmap convert failure"); + return false; + } + } + else + { + /* TODO embed albumart does not support non-JPEG formats */ + logf("does not support image format: %d", id3->albumart.type); + return false; + } + embed_aa.usable = true; + return true; +} + int playback_current_aa_hid(int slot) { - if (slot < 0) - return -1; int cur_idx; int offset = ci.new_track + wps_offset; + if (slot < 0) + return -1; + + if (embed_aa.usable) + return embed_aa.hid; + 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; + + /* set the embed albumart's width and height */ + embed_aa.dim = *dim; + open_embed_albumart(); + /* 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) @@ -692,8 +780,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--; } @@ -1006,6 +1098,8 @@ static void audio_update_trackinfo(void) ci.id3 = thistrack_id3; ci.curpos = 0; ci.taginfo_ready = &CUR_TI->taginfo_ready; + + regist_embed_albumart(ci.id3); } /* Clear tracks between write and read, non inclusive */ @@ -1292,34 +1386,37 @@ static void audio_finish_load_track(void) { int i; char aa_path[MAX_PATH]; - FOREACH_ALBUMART(i) + + if (track_ridx != track_widx || !regist_embed_albumart(track_id3)) { - /* albumart_slots may change during a yield of bufopen, - * but that's no problem */ - 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), - &(albumart_slots[i].dim))) + FOREACH_ALBUMART(i) { - int aa_hid = bufopen(aa_path, 0, TYPE_BITMAP, - &(albumart_slots[i].dim)); - - if(aa_hid == ERR_BUFFER_FULL) - { - filling = STATE_FULL; - logf("buffer is full for now (get album art)"); - return; /* No space for track's album art, not an error */ - } - else if (aa_hid < 0) + /* albumart_slots may change during a yield of bufopen, + * but that's no problem */ + 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), + &(albumart_slots[i].dim))) { - /* another error, ignore AlbumArt */ - logf("Album art loading failed"); + int aa_hid = bufopen(aa_path, 0, TYPE_BITMAP, + &(albumart_slots[i].dim)); + + if(aa_hid == ERR_BUFFER_FULL) + { + filling = STATE_FULL; + 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) + { + /* another error, ignore AlbumArt */ + logf("Album art loading failed"); + } + tracks[track_widx].aa_hid[i] = aa_hid; } - tracks[track_widx].aa_hid[i] = aa_hid; } } - } #endif @@ -1448,7 +1545,12 @@ 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(); +#ifdef HAVE_ALBUMART + open_embed_albumart(); +#endif + } logf("Starting buffer fill"); @@ -2026,6 +2128,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 @@ -2123,6 +2229,9 @@ void audio_init(void) tracks[j].aa_hid[i] = -1; } } + + embed_aa.hid = -1; + embed_aa.usable = false; #endif add_event(BUFFER_EVENT_REBUFFER, false, buffering_handle_rebuffer_callback); diff --git a/apps/plugin.c b/apps/plugin.c index d626ef6..adffd8e 100644 --- a/apps/plugin.c +++ b/apps/plugin.c @@ -655,8 +655,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 a6b864b..c1ec67c 100644 --- a/apps/plugin.h +++ b/apps/plugin.h @@ -141,15 +141,16 @@ void* plugin_get_buffer(size_t *buffer_size); #endif + #define PLUGIN_MAGIC 0x526F634B /* RocK */ /* increase this every time the api struct changes */ -#define PLUGIN_API_VERSION 187 +#define PLUGIN_API_VERSION 188 /* update this to latest version if a change to the api struct breaks backwards compatibility (and please take the opportunity to sort in any new function which are "waiting" at the end of the function table) */ -#define PLUGIN_MIN_API_VERSION 187 +#define PLUGIN_MIN_API_VERSION 188 /* plugin return codes */ enum plugin_status { @@ -812,9 +813,10 @@ int (*round_value_to_list32)(unsigned long value, 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,