metadata: mp3: Support unsync embedded jpeg album art

Support parsing alubm art from id3 metadata with "unsynchronisation scheme":
https://id3.org/id3v2.3.0#The_unsynchronisation_scheme

Change-Id: I1e2ca4ae0aa967f7e80142a04c9a7d99e38e68b2
This commit is contained in:
Roman Artiukhin 2024-11-06 22:28:24 +02:00 committed by Solomon Peachy
parent 6649731563
commit 7f4a8891a6
7 changed files with 72 additions and 23 deletions

View file

@ -867,7 +867,7 @@ static int load_image(int fd, const char *path,
#ifdef HAVE_JPEG #ifdef HAVE_JPEG
if (aa != NULL) { if (aa != NULL) {
lseek(fd, aa->pos, SEEK_SET); lseek(fd, aa->pos, SEEK_SET);
rc = clip_jpeg_fd(fd, aa->size, bmp, (int)max_size, format, NULL); rc = clip_jpeg_fd(fd, aa->type, aa->size, bmp, (int)max_size, format, NULL);
} }
else if (strcmp(path + strlen(path) - 4, ".bmp")) else if (strcmp(path + strlen(path) - 4, ".bmp"))
rc = read_jpeg_fd(fd, bmp, (int)max_size, format, NULL); rc = read_jpeg_fd(fd, bmp, (int)max_size, format, NULL);

View file

@ -1922,7 +1922,7 @@ static int audio_load_albumart(struct track_info *infop,
/* We can only decode jpeg for embedded AA */ /* We can only decode jpeg for embedded AA */
if (global_settings.album_art != AA_OFF && if (global_settings.album_art != AA_OFF &&
hid < 0 && hid != ERR_BUFFER_FULL && hid < 0 && hid != ERR_BUFFER_FULL &&
track_id3->has_embedded_albumart && track_id3->albumart.type == AA_TYPE_JPG) track_id3->has_embedded_albumart && (track_id3->albumart.type & AA_CLEAR_FLAGS_MASK) == AA_TYPE_JPG)
{ {
if (is_current_track) if (is_current_track)
clear_last_folder_album_art(); clear_last_folder_album_art();

View file

@ -26,7 +26,7 @@
* KIND, either express or implied. * KIND, either express or implied.
* *
****************************************************************************/ ****************************************************************************/
#include "embedded_metadata.h"
#include "plugin.h" #include "plugin.h"
#include "debug.h" #include "debug.h"
#include "jpeg_load.h" #include "jpeg_load.h"
@ -79,6 +79,9 @@ struct jpeg
int fd; int fd;
int buf_left; int buf_left;
int buf_index; int buf_index;
int (*read_buf)(struct jpeg* p_jpeg, size_t count);
void* custom_param;
#endif #endif
unsigned long len; unsigned long len;
unsigned long int bitbuf; unsigned long int bitbuf;
@ -872,11 +875,21 @@ INLINE void jpeg_putc(struct jpeg* p_jpeg)
p_jpeg->data--; p_jpeg->data--;
} }
#else #else
static int read_buf(struct jpeg* p_jpeg, size_t count)
{
return read(p_jpeg->fd, p_jpeg->buf, count);
}
static int read_buf_id3_unsync(struct jpeg* p_jpeg, size_t count)
{
count = read(p_jpeg->fd, p_jpeg->buf, count);
return id3_unsynchronize(p_jpeg->buf, count, (bool*) &p_jpeg->custom_param);
}
INLINE void fill_buf(struct jpeg* p_jpeg) INLINE void fill_buf(struct jpeg* p_jpeg)
{ {
p_jpeg->buf_left = read(p_jpeg->fd, p_jpeg->buf, p_jpeg->buf_left = p_jpeg->read_buf(p_jpeg, MIN(JPEG_READ_BUF_SIZE, p_jpeg->len));
(p_jpeg->len >= JPEG_READ_BUF_SIZE)?
JPEG_READ_BUF_SIZE : p_jpeg->len);
p_jpeg->buf_index = 0; p_jpeg->buf_index = 0;
if (p_jpeg->buf_left > 0) if (p_jpeg->buf_left > 0)
p_jpeg->len -= p_jpeg->buf_left; p_jpeg->len -= p_jpeg->buf_left;
@ -1958,7 +1971,7 @@ int clip_jpeg_file(const char* filename,
return fd * 10 - 1; return fd * 10 - 1;
} }
lseek(fd, offset, SEEK_SET); lseek(fd, offset, SEEK_SET);
ret = clip_jpeg_fd(fd, jpeg_size, bm, maxsize, format, cformat); ret = clip_jpeg_fd(fd, 0, jpeg_size, bm, maxsize, format, cformat);
close(fd); close(fd);
return ret; return ret;
} }
@ -2007,7 +2020,7 @@ int get_jpeg_dim_mem(unsigned char *data, unsigned long len,
int decode_jpeg_mem(unsigned char *data, int decode_jpeg_mem(unsigned char *data,
#else #else
int clip_jpeg_fd(int fd, int clip_jpeg_fd(int fd, int flags,
#endif #endif
unsigned long len, unsigned long len,
struct bitmap *bm, struct bitmap *bm,
@ -2038,6 +2051,14 @@ int clip_jpeg_fd(int fd,
p_jpeg->fd = fd; p_jpeg->fd = fd;
if (p_jpeg->len == 0) if (p_jpeg->len == 0)
p_jpeg->len = filesize(p_jpeg->fd); p_jpeg->len = filesize(p_jpeg->fd);
p_jpeg->read_buf = read_buf;
if (flags & AA_FLAG_ID3_UNSYNC)
{
p_jpeg->read_buf = read_buf_id3_unsync;
p_jpeg->custom_param = false;
}
#endif #endif
status = process_markers(p_jpeg); status = process_markers(p_jpeg);
#ifndef JPEG_FROM_MEM #ifndef JPEG_FROM_MEM
@ -2225,7 +2246,7 @@ int read_jpeg_fd(int fd,
int format, int format,
const struct custom_format *cformat) const struct custom_format *cformat)
{ {
return clip_jpeg_fd(fd, 0, bm, maxsize, format, cformat); return clip_jpeg_fd(fd, 0, 0, bm, maxsize, format, cformat);
} }
#endif #endif

View file

@ -67,7 +67,7 @@ int clip_jpeg_file(const char* filename,
* read embedded jpeg files as above. Needs an open file descripter, and * read embedded jpeg files as above. Needs an open file descripter, and
* assumes the caller has lseek()'d to the start of the jpeg blob * assumes the caller has lseek()'d to the start of the jpeg blob
**/ **/
int clip_jpeg_fd(int fd, int clip_jpeg_fd(int fd, int flags,
unsigned long jpeg_size, unsigned long jpeg_size,
struct bitmap *bm, struct bitmap *bm,
int maxsize, int maxsize,

View file

@ -0,0 +1,23 @@
/***************************************************************************
* __________ __ ___.
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
* \/ \/ \/ \/ \/
* $Id$
*
* Copyright (C) 2024 Roman Artiukhin
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
****************************************************************************/
#include <stdbool.h>
int id3_unsynchronize(char* tag, int len, bool *ff_found);

View file

@ -45,6 +45,7 @@
#include "mp3data.h" #include "mp3data.h"
#include "metadata_common.h" #include "metadata_common.h"
#include "metadata_parsers.h" #include "metadata_parsers.h"
#include "embedded_metadata.h"
#include "misc.h" #include "misc.h"
static unsigned long unsync(unsigned long b0, static unsigned long unsync(unsigned long b0,
@ -161,7 +162,8 @@ struct tag_resolver {
static bool global_ff_found; static bool global_ff_found;
static int unsynchronize(char* tag, int len, bool *ff_found) #define unsynchronize id3_unsynchronize
int id3_unsynchronize(char* tag, int len, bool *ff_found)
{ {
int i; int i;
unsigned char c; unsigned char c;
@ -299,9 +301,7 @@ static int parsealbumart( struct mp3entry* entry, char* tag, int bufferpos )
if(entry->has_embedded_albumart) if(entry->has_embedded_albumart)
return bufferpos; return bufferpos;
/* we currently don't support unsynchronizing albumart */ bool unsync = entry->albumart.type == AA_FLAG_ID3_UNSYNC;
if (entry->albumart.type == AA_TYPE_UNSYNC)
return bufferpos;
entry->albumart.type = AA_TYPE_UNKNOWN; entry->albumart.type = AA_TYPE_UNKNOWN;
@ -353,6 +353,10 @@ static int parsealbumart( struct mp3entry* entry, char* tag, int bufferpos )
/* fixup offset&size for image data */ /* fixup offset&size for image data */
entry->albumart.pos += tag - start; entry->albumart.pos += tag - start;
entry->albumart.size -= tag - start; entry->albumart.size -= tag - start;
if (unsync)
{
entry->albumart.type |= AA_FLAG_ID3_UNSYNC;
}
/* check for malformed tag with no picture data */ /* check for malformed tag with no picture data */
entry->has_embedded_albumart = (entry->albumart.size != 0); entry->has_embedded_albumart = (entry->albumart.size != 0);
} }
@ -1141,15 +1145,12 @@ retry_with_limit:
if ((!entry->has_embedded_albumart) && if ((!entry->has_embedded_albumart) &&
((tr->tag_length == 4 && !memcmp( header, "APIC", 4)) || ((tr->tag_length == 4 && !memcmp( header, "APIC", 4)) ||
(tr->tag_length == 3 && !memcmp( header, "PIC" , 3)))) (tr->tag_length == 3 && !memcmp( header, "PIC" , 3))))
{
if (unsynch || (global_unsynch && version <= ID3_VER_2_3))
entry->albumart.type = AA_TYPE_UNSYNC;
else
{ {
entry->albumart.pos = lseek(fd, 0, SEEK_CUR) - framelen; entry->albumart.pos = lseek(fd, 0, SEEK_CUR) - framelen;
entry->albumart.size = totframelen; entry->albumart.size = totframelen;
entry->albumart.type = AA_TYPE_UNKNOWN; entry->albumart.type = (unsynch || (global_unsynch && version <= ID3_VER_2_3))
} ? AA_FLAG_ID3_UNSYNC
: AA_TYPE_UNKNOWN;
} }
#endif #endif
if( tr->ppFunc ) if( tr->ppFunc )

View file

@ -196,13 +196,17 @@ enum {
ID3_VER_2_4 ID3_VER_2_4
}; };
#define AA_FLAGS_SHIFT 4
#define AA_CLEAR_FLAGS_MASK ~(-1 << AA_FLAGS_SHIFT)
#ifdef HAVE_ALBUMART #ifdef HAVE_ALBUMART
enum mp3_aa_type { enum mp3_aa_type {
AA_TYPE_UNSYNC = -1,
AA_TYPE_UNKNOWN, AA_TYPE_UNKNOWN,
AA_TYPE_BMP, AA_TYPE_BMP,
AA_TYPE_PNG, AA_TYPE_PNG,
AA_TYPE_JPG, AA_TYPE_JPG,
AA_FLAG_ID3_UNSYNC = 1 << (AA_FLAGS_SHIFT + 0),
}; };
struct mp3_albumart { struct mp3_albumart {