music_mad.c: handle and skip tags (bug #4295). SDL-1.2
authorOzkan Sezer <sezeroz@gmail.com>
Sun, 07 Oct 2018 11:47:50 +0300
branchSDL-1.2
changeset 890ae9b46ccd5ab
parent 889 673c5875d64d
child 892 9dd046cb6cd7
music_mad.c: handle and skip tags (bug #4295).
music_mad.c
     1.1 --- a/music_mad.c	Sun Oct 07 11:40:55 2018 +0300
     1.2 +++ b/music_mad.c	Sun Oct 07 11:47:50 2018 +0300
     1.3 @@ -79,6 +79,101 @@
     1.4    return ((mp3_mad->status & MS_playing) != 0);
     1.5  }
     1.6  
     1.7 +
     1.8 +/*************************** TAG HANDLING: ******************************/
     1.9 +
    1.10 +static __inline__ SDL_bool is_id3v1(const Uint8 *data, size_t length)
    1.11 +{
    1.12 +    /* http://id3.org/ID3v1 :  3 bytes "TAG" identifier and 125 bytes tag data */
    1.13 +    if (length < 3 || SDL_memcmp(data,"TAG",3) != 0) {
    1.14 +        return SDL_FALSE;
    1.15 +    }
    1.16 +    return SDL_TRUE;
    1.17 +}
    1.18 +static __inline__ SDL_bool is_id3v2(const Uint8 *data, size_t length)
    1.19 +{
    1.20 +    /* ID3v2 header is 10 bytes:  http://id3.org/id3v2.4.0-structure */
    1.21 +    /* bytes 0-2: "ID3" identifier */
    1.22 +    if (length < 10 || SDL_memcmp(data,"ID3",3) != 0) {
    1.23 +        return SDL_FALSE;
    1.24 +    }
    1.25 +    /* bytes 3-4: version num (major,revision), each byte always less than 0xff. */
    1.26 +    if (data[3] == 0xff || data[4] == 0xff) {
    1.27 +        return SDL_FALSE;
    1.28 +    }
    1.29 +    /* bytes 6-9 are the ID3v2 tag size: a 32 bit 'synchsafe' integer, i.e. the
    1.30 +     * highest bit 7 in each byte zeroed.  i.e.: 7 bit information in each byte ->
    1.31 +     * effectively a 28 bit value.  */
    1.32 +    if (data[6] >= 0x80 || data[7] >= 0x80 || data[8] >= 0x80 || data[9] >= 0x80) {
    1.33 +        return SDL_FALSE;
    1.34 +    }
    1.35 +    return SDL_TRUE;
    1.36 +}
    1.37 +static __inline__ SDL_bool is_apetag(const Uint8 *data, size_t length)
    1.38 +{
    1.39 +   /* http://wiki.hydrogenaud.io/index.php?title=APEv2_specification
    1.40 +    * APEv2 header is 32 bytes: bytes 0-7 ident, bytes 8-11 version,
    1.41 +    * bytes 12-17 size.  bytes 24-31 are reserved: must be all zeroes.
    1.42 +    * APEv1 has no header, so no luck.  */
    1.43 +    Uint32 v;
    1.44 +
    1.45 +    if (length < 32 || SDL_memcmp(data,"APETAGEX",8) != 0) {
    1.46 +        return SDL_FALSE;
    1.47 +    }
    1.48 +    v = (data[11]<<24) | (data[10]<<16) | (data[9]<<8) | data[8]; /* version */
    1.49 +    if (v != 2000U) {
    1.50 +        return SDL_FALSE;
    1.51 +    }
    1.52 +    v = 0; /* reserved bits : */
    1.53 +    if (SDL_memcmp(&data[24],&v,4) != 0 || SDL_memcmp(&data[28],&v,4) != 0) {
    1.54 +        return SDL_FALSE;
    1.55 +    }
    1.56 +    return SDL_TRUE;
    1.57 +}
    1.58 +
    1.59 +static size_t get_tagsize(const Uint8 *data, size_t length)
    1.60 +{
    1.61 +    size_t size;
    1.62 +
    1.63 +    if (is_id3v1(data, length)) {
    1.64 +        return 128; /* fixed length */
    1.65 +    }
    1.66 +    if (is_id3v2(data, length)) {
    1.67 +        /* size is a 'synchsafe' integer (see above) */
    1.68 +        size = (data[6]<<21) + (data[7]<<14) + (data[8]<<7) + data[9];
    1.69 +        size += 10; /* header size */
    1.70 +        /* ID3v2 header[5] is flags (bits 4-7 only, 0-3 are zero).
    1.71 +         * bit 4 set: footer is present (a copy of the header but
    1.72 +         * with "3DI" as ident.)  */
    1.73 +        if (data[5] & 0x10) {
    1.74 +            size += 10; /* footer size */
    1.75 +        }
    1.76 +        /* optional padding (always zeroes) */
    1.77 +        while (size < length && data[size] == 0) {
    1.78 +            ++size;
    1.79 +        }
    1.80 +        return size;
    1.81 +    }
    1.82 +    if (is_apetag(data, length)) {
    1.83 +        size = (data[15]<<24) | (data[14]<<16) | (data[13]<<8) | data[12];
    1.84 +        size += 32; /* header size */
    1.85 +        return size;
    1.86 +    }
    1.87 +    return 0;
    1.88 +}
    1.89 +
    1.90 +static int consume_tag(struct mad_stream *stream)
    1.91 +{
    1.92 +    /* FIXME: what if the buffer doesn't have the full tag ??? */
    1.93 +    size_t remaining = stream->bufend - stream->next_frame;
    1.94 +    size_t tagsize = get_tagsize(stream->this_frame, remaining);
    1.95 +    if (tagsize != 0) {
    1.96 +        mad_stream_skip(stream, tagsize);
    1.97 +        return 0;
    1.98 +    }
    1.99 +    return -1;
   1.100 +}
   1.101 +
   1.102  /* Reads the next frame from the file.  Returns true on success or
   1.103     false on failure. */
   1.104  static int
   1.105 @@ -132,6 +227,8 @@
   1.106  	 its buffer. */
   1.107    if (mad_frame_decode(&mp3_mad->frame, &mp3_mad->stream)) {
   1.108  	if (MAD_RECOVERABLE(mp3_mad->stream.error)) {
   1.109 +	  consume_tag(&mp3_mad->stream); /* consume any ID3 tags */
   1.110 +	  mad_stream_sync(&mp3_mad->stream); /* to frame seek mode */
   1.111  	  return 0;
   1.112  	  
   1.113  	} else if (mp3_mad->stream.error == MAD_ERROR_BUFLEN) {