From f97fa5e6ba43baa8a67245ce0223836e5175cbd9 Mon Sep 17 00:00:00 2001 From: Arthur Taylor Date: Mon, 22 Mar 2021 11:05:57 -0700 Subject: [PATCH] id3: Remember id3v2 Header Info. Do Call mpeg_decode() Twice. --- src/common.c | 4 ++++ src/common.h | 8 ++++++++ src/id3.c | 24 ++++++++++++++++-------- src/mpeg_decode.c | 31 ++++++++++++++++++------------- src/sndfile.c | 11 ----------- 5 files changed, 46 insertions(+), 32 deletions(-) diff --git a/src/common.c b/src/common.c index a198c28d..b8cc2c19 100644 --- a/src/common.c +++ b/src/common.c @@ -1136,6 +1136,10 @@ psf_binheader_readf (SF_PRIVATE *psf, char const *format, ...) byte_count += count ; break ; + case '!' : /* Clear buffer, forcing re-read. */ + psf->header.end = psf->header.indx = 0 ; + break ; + default : psf_log_printf (psf, "*** Invalid format specifier `%c'\n", c) ; psf->error = SFE_INTERNAL ; diff --git a/src/common.h b/src/common.h index bc7df5ca..6ca0648f 100644 --- a/src/common.h +++ b/src/common.h @@ -296,6 +296,12 @@ typedef SF_BROADCAST_INFO_VAR (16 * 1024) SF_BROADCAST_INFO_16K ; typedef SF_CART_INFO_VAR (16 * 1024) SF_CART_INFO_16K ; +typedef struct +{ sf_count_t offset ; + sf_count_t len ; + unsigned minor_version ; +} ID3V2_HEADER_INFO ; + #if SIZEOF_WCHAR_T == 2 typedef wchar_t sfwchar_t ; #else @@ -516,6 +522,8 @@ typedef struct sf_private_tag int (*get_chunk_data) (struct sf_private_tag*, const SF_CHUNK_ITERATOR * iterator, SF_CHUNK_INFO * chunk_info) ; int cpu_flags ; + + ID3V2_HEADER_INFO id3_header ; } SF_PRIVATE ; diff --git a/src/id3.c b/src/id3.c index 6302f5b0..1516ee31 100644 --- a/src/id3.c +++ b/src/id3.c @@ -63,31 +63,39 @@ id3_lookup_v1_genre (int UNUSED (number)) #endif - int id3_skip (SF_PRIVATE * psf) { unsigned char buf [10] ; + int offset ; memset (buf, 0, sizeof (buf)) ; psf_binheader_readf (psf, "pb", 0, buf, 10) ; if (buf [0] == 'I' && buf [1] == 'D' && buf [2] == '3') - { int offset = buf [6] & 0x7f ; + { psf->id3_header.minor_version = buf [3] ; + offset = buf [6] & 0x7f ; offset = (offset << 7) | (buf [7] & 0x7f) ; offset = (offset << 7) | (buf [8] & 0x7f) ; offset = (offset << 7) | (buf [9] & 0x7f) ; - psf_log_printf (psf, "ID3 length : %d\n--------------------\n", offset) ; + /* + ** ID3 count field is how many bytes of ID3v2 header FOLLOW the ten + ** bytes of header magic and offset, NOT the total ID3v2 header len. + */ + psf->id3_header.len = offset + 10 ; + psf->id3_header.offset = psf->fileoffset ; + + psf_log_printf (psf, " ID3v2.%d header length : %d\n----------------------------------------\n", + psf->id3_header.minor_version, psf->id3_header.len) ; /* Never want to jump backwards in a file. */ if (offset < 0) return 0 ; - /* Calculate new file offset and position ourselves there. */ - offset += 10 ; - if (psf->fileoffset + offset < psf->filelength) - { psf_binheader_readf (psf, "p", offset) ; - psf->fileoffset += offset ; + /* Position ourselves at the new file offset. */ + if (psf->fileoffset + psf->id3_header.len < psf->filelength) + { psf_binheader_readf (psf, "p!", psf->id3_header.len) ; + psf->fileoffset += psf->id3_header.len ; return 1 ; } ; } ; diff --git a/src/mpeg_decode.c b/src/mpeg_decode.c index bf0a827e..64efa564 100644 --- a/src/mpeg_decode.c +++ b/src/mpeg_decode.c @@ -526,17 +526,6 @@ mpeg_decoder_init (SF_PRIVATE *psf) if (! (psf->file.mode & SFM_READ)) return SFE_INTERNAL ; - /* - ** Check to see if we have already successfully opened the file when we - ** guessed it was an MP3 based on seeing an ID3 header. - */ - if (psf->codec_data != NULL && - (SF_CODEC (psf->sf.format) == SF_FORMAT_MPEG_LAYER_I - || SF_CODEC (psf->sf.format) == SF_FORMAT_MPEG_LAYER_II - || SF_CODEC (psf->sf.format) == SF_FORMAT_MPEG_LAYER_III) - && ((MPEG_DEC_PRIVATE *) psf->codec_data)->unique_id == psf->unique_id) - return 0 ; - /* ** *** FIXME - Threading issues *** ** @@ -580,15 +569,31 @@ mpeg_decoder_init (SF_PRIVATE *psf) #endif psf->dataoffset = 0 ; - + /* + ** Need to pass the first MPEG frame to libmpg123, but that frame was read + ** into psf->binheader in order that we could identify the stream. + */ if (psf->is_pipe) - { // Need to read data in the binheader buffer. Avoid a file seek. + { /* + ** Can't seek, so setup our libmpg123 io callbacks to read the binheader + ** buffer first. + */ psf_binheader_readf (psf, "p", 0) ; pmp3d->header_remaining = psf_binheader_readf (psf, NULL) ; + + /* Tell libmpg123 we can't seek the file. */ mpg123_param (pmp3d->pmh, MPG123_ADD_FLAGS, MPG123_NO_PEEK_END, 1.0) ; } else + { /* + ** libmpg123 can parse the ID3v2 header. Undo the embedded file offset if the + ** enclosing file data is the ID3v2 header. + */ + if (psf->id3_header.len > 0 && psf->id3_header.len + psf->id3_header.offset == psf->fileoffset) + psf->fileoffset = psf->id3_header.offset ; + psf_fseek (psf, 0, SEEK_SET) ; + } ; error = mpg123_open_handle (pmp3d->pmh, psf) ; if (error != MPG123_OK) diff --git a/src/sndfile.c b/src/sndfile.c index 58df240a..365c1361 100644 --- a/src/sndfile.c +++ b/src/sndfile.c @@ -967,7 +967,6 @@ sf_format_check (const SF_INFO *info) return 0 ; if (endian != SF_ENDIAN_FILE) return 0 ; - /* TODO */ if (subformat == SF_FORMAT_MPEG_LAYER_I || subformat == SF_FORMAT_MPEG_LAYER_II || subformat == SF_FORMAT_MPEG_LAYER_III) return 1 ; break ; @@ -2881,16 +2880,6 @@ retry: if (buffer [0] == MAKE_MARKER ('I', 'D', '3', 2) || buffer [0] == MAKE_MARKER ('I', 'D', '3', 3) || buffer [0] == MAKE_MARKER ('I', 'D', '3', 4)) { psf_log_printf (psf, "Found 'ID3' marker.\n") ; - - /* Guess MP3, try and open it as such. Allows libmpg123 to parse the ID3v2 headers */ - if (psf->file.mode == SFM_READ) - { if (mpeg_open (psf) == 0) - return SF_FORMAT_MPEG | ((~SF_FORMAT_TYPEMASK) & psf->sf.format) ; - else if (psf->codec_close) - psf->codec_close (psf) ; - } ; - - /* Otherwise, seek to after the ID3 header */ if (id3_skip (psf)) goto retry ; return 0 ;