id3: fix bitrate to be multiple of 1000.
[platform/upstream/lightmediascanner.git] / src / plugins / id3 / id3.c
index 218259d..b4ba169 100644 (file)
  * @brief
  *
  * id3 file parser.
+ *
+ * Reference:
+ *   http://www.mp3-tech.org/programmer/frame_header.html
+ *   http://www.mpgedit.org/mpgedit/mpeg_format/MP3Format.html
+ *   http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
+ *   http://id3.org/id3v2.3.0
  */
 
-#ifdef HAVE_CONFIG_H
-#include "config.h"
-#endif
-
-#define _GNU_SOURCE
-#define _XOPEN_SOURCE 600
 #include <lightmediascanner_plugin.h>
 #include <lightmediascanner_db.h>
 #include <lightmediascanner_charset_conv.h>
+#include <shared/util.h>
+
 #include <sys/types.h>
 #include <sys/stat.h>
+#include <assert.h>
 #include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <unistd.h>
 #include <ctype.h>
 
+#define DECL_STR(cname, str)                                            \
+    static const struct lms_string_size cname = LMS_STATIC_STRING_SIZE(str)
+
+DECL_STR(_codec_mpeg1layer1, "mpeg1layer1");
+DECL_STR(_codec_mpeg1layer2, "mpeg1layer2");
+DECL_STR(_codec_mpeg1layer3, "mpeg1layer3");
+DECL_STR(_codec_mpeg2layer1, "mpeg2layer1");
+DECL_STR(_codec_mpeg2layer2, "mpeg2layer2");
+DECL_STR(_codec_mpeg2layer3, "mpeg2layer3");
+DECL_STR(_codec_mpeg2_5layer1, "mpeg2.5layer1");
+DECL_STR(_codec_mpeg2_5layer2, "mpeg2.5layer2");
+DECL_STR(_codec_mpeg2_5layer3, "mpeg2.5layer3");
+DECL_STR(_codec_mpeg2aac_main, "mpeg2aac-main");
+DECL_STR(_codec_mpeg2aac_lc, "mpeg2aac-lc");
+DECL_STR(_codec_mpeg2aac_ssr, "mpeg2aac-ssr");
+DECL_STR(_codec_mpeg2aac_ltp, "mpeg2aac-ltp");
+DECL_STR(_codec_mpeg4aac_main, "mpeg4aac-main");
+DECL_STR(_codec_mpeg4aac_lc, "mpeg4aac-lc");
+DECL_STR(_codec_mpeg4aac_ssr, "mpeg4aac-ssr");
+DECL_STR(_codec_mpeg4aac_ltp, "mpeg4aac-ltp");
+
+DECL_STR(_dlna_mp3, "MP3");
+DECL_STR(_dlna_mp3x, "MP3X");
+
+DECL_STR(_dlna_aac_adts_320, "AAC_ADTS_320");
+DECL_STR(_dlna_aac_adts, "AAC_ADTS");
+DECL_STR(_dlna_aac_adts_mult5, "AAC_MULT5_ADTS");
+
+DECL_STR(_dlna_mime_mpeg, "audio/mpeg");
+DECL_STR(_dlna_mime_adts, "audio/vnd.dlna.adts");
+#undef DECL_STR
+
+
+static void
+_fill_dlna_profile(struct lms_audio_info *info)
+{
+    if ((info->channels == 1 || info->channels == 2) &&
+        (info->sampling_rate == 32000 ||
+         info->sampling_rate == 44100 ||
+         info->sampling_rate == 48000) &&
+        (info->bitrate >= 32000 && info->bitrate <= 320000) &&
+        info->codec.str == _codec_mpeg1layer3.str) {
+        info->dlna_profile = _dlna_mp3;
+        info->dlna_mime = _dlna_mime_mpeg;
+   } else if (
+        (info->channels == 1 || info->channels == 2) &&
+        (info->sampling_rate == 16000 ||
+         info->sampling_rate == 22050 ||
+         info->sampling_rate == 24000 ||
+         info->sampling_rate == 32000 ||
+         info->sampling_rate == 44100 ||
+         info->sampling_rate == 48000) &&
+        (info->bitrate >= 8000 && info->bitrate <= 320000) &&
+        (info->codec.str == _codec_mpeg1layer1.str ||
+         info->codec.str == _codec_mpeg1layer2.str ||
+         info->codec.str == _codec_mpeg1layer3.str ||
+         info->codec.str == _codec_mpeg2layer1.str ||
+         info->codec.str == _codec_mpeg2layer2.str ||
+         info->codec.str == _codec_mpeg2layer3.str)) {
+        info->dlna_profile = _dlna_mp3x;
+        info->dlna_mime = _dlna_mime_mpeg;
+    } else if (
+        (info->sampling_rate == 8000 ||
+         info->sampling_rate == 11025 ||
+         info->sampling_rate == 12000 ||
+         info->sampling_rate == 16000 ||
+         info->sampling_rate == 22050 ||
+         info->sampling_rate == 24000 ||
+         info->sampling_rate == 32000 ||
+         info->sampling_rate == 44100 ||
+         info->sampling_rate == 48000) &&
+        (info->codec.str == _codec_mpeg2aac_lc.str ||
+         info->codec.str == _codec_mpeg4aac_lc.str)) {
+
+        if (info->channels == 1 || info->channels == 2) {
+            if (info->bitrate <= 320000) {
+                info->dlna_profile = _dlna_aac_adts_320;
+                info->dlna_mime = _dlna_mime_adts;
+            } else if (info->bitrate <= 576000) {
+                info->dlna_profile = _dlna_aac_adts;
+                info->dlna_mime = _dlna_mime_adts;
+            }
+        } else if ((info->channels >= 1 && info->channels <= 6) &&
+                   (info->bitrate <= 1440000)) {
+            info->dlna_profile = _dlna_aac_adts_mult5;
+            info->dlna_mime = _dlna_mime_adts;
+        }
+    }
+}
+
 #define ID3V2_HEADER_SIZE       10
 #define ID3V2_FOOTER_SIZE       10
 
+#define MPEG_HEADER_SIZE 4
+
+/* TODO: The higher these numbers are, the more performance impact you get
+ * when parsing mp3. However the lower they are, the more imprecise bitrate
+ * _and_ length estimate will be. Investigate which would be the best numbers
+ * here. */
+#define N_FRAMES_BITRATE_ESTIMATE 512
+#define N_FRAMES_CBR_ESTIMATE 128
+
+enum mpeg_audio_version {
+    MPEG_AUDIO_VERSION_1,
+    MPEG_AUDIO_VERSION_2,
+    MPEG_AUDIO_VERSION_2_5,
+    MPEG_AUDIO_VERSION_4,
+    _MPEG_AUDIO_VERSION_COUNT
+};
+
+enum mpeg_audio_layer {
+    MPEG_AUDIO_LAYER_1,
+    MPEG_AUDIO_LAYER_2,
+    MPEG_AUDIO_LAYER_3,
+    MPEG_AUDIO_LAYER_AAC,
+    _MPEG_AUDIO_LAYER_COUNT
+};
+
+struct mpeg_header {
+    enum mpeg_audio_version version;
+    enum mpeg_audio_layer layer;
+
+    bool crc;
+    bool padding;
+    uint8_t channels;
+    uint8_t sampling_rate_idx;
+    uint8_t codec_idx;
+    unsigned int bitrate_idx;
+
+    unsigned int bitrate;
+    unsigned int length;
+    bool cbr;
+};
+
+static const struct lms_string_size *_codecs[] = {
+    /* mp3 */
+    [0] = &_codec_mpeg1layer1,
+    [1] = &_codec_mpeg1layer2,
+    [2] = &_codec_mpeg1layer3,
+    [3] = &_codec_mpeg2layer1,
+    [4] = &_codec_mpeg2layer2,
+    [5] = &_codec_mpeg2layer3,
+    [6] = &_codec_mpeg2_5layer1,
+    [7] = &_codec_mpeg2_5layer2,
+    [8] = &_codec_mpeg2_5layer3,
+
+    /* aac */
+#define MPEG_CODEC_AAC_START 9
+    [9] = &_codec_mpeg2aac_main,
+    [10] = &_codec_mpeg2aac_lc,
+    [11] = &_codec_mpeg2aac_ssr,
+    [12] = &_codec_mpeg2aac_ltp,
+
+    [13] = &_codec_mpeg4aac_main,
+    [14] = &_codec_mpeg4aac_lc,
+    [15] = &_codec_mpeg4aac_ssr,
+    [16] = &_codec_mpeg4aac_ltp,
+    [17] = NULL
+};
+
+/* Ordered according to AAC index, take care with mp3 */
+static int _sample_rates[16] = {
+    96000, 88200, 64000,
+
+    /* Frequencies available on mp3, */
+    48000, 44100, 32000,
+    24000, 22050, 16000,
+    12000, 11025, 8000,
+
+    7350, /* reserved, zeroed */
+};
+
+static unsigned int
+_bitrate_table[_MPEG_AUDIO_VERSION_COUNT][_MPEG_AUDIO_LAYER_COUNT][16] = {
+    [MPEG_AUDIO_VERSION_1][MPEG_AUDIO_LAYER_1] = {
+        0,32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0},
+    [MPEG_AUDIO_VERSION_1][MPEG_AUDIO_LAYER_2] = {
+        0,32, 48, 56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 384, 0},
+    [MPEG_AUDIO_VERSION_1][MPEG_AUDIO_LAYER_3] = {
+        0,32, 40, 48,  56,  64,  80,  96, 112, 128, 160, 192, 224, 256, 320, 0},
+    [MPEG_AUDIO_VERSION_2][MPEG_AUDIO_LAYER_2] = {
+        0,32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
+    [MPEG_AUDIO_VERSION_2][MPEG_AUDIO_LAYER_3] = {
+        0, 8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0},
+    [MPEG_AUDIO_VERSION_2_5][MPEG_AUDIO_LAYER_2] = {
+        0,32, 48, 56,  64,  80,  96, 112, 128, 144, 160, 176, 192, 224, 256, 0},
+    [MPEG_AUDIO_VERSION_2_5][MPEG_AUDIO_LAYER_3] = {
+        0, 8, 16, 24,  32,  40,  48,  56,  64,  80,  96, 112, 128, 144, 160, 0},
+};
+
+static unsigned int
+_samples_per_frame_table[_MPEG_AUDIO_VERSION_COUNT][_MPEG_AUDIO_LAYER_COUNT] = {
+    [MPEG_AUDIO_VERSION_1][MPEG_AUDIO_LAYER_1] = 384,
+    [MPEG_AUDIO_VERSION_1][MPEG_AUDIO_LAYER_2] = 1152,
+    [MPEG_AUDIO_VERSION_1][MPEG_AUDIO_LAYER_3] = 1152,
+    [MPEG_AUDIO_VERSION_2][MPEG_AUDIO_LAYER_1] = 384,
+    [MPEG_AUDIO_VERSION_2][MPEG_AUDIO_LAYER_2] = 1152,
+    [MPEG_AUDIO_VERSION_2][MPEG_AUDIO_LAYER_3] = 576,
+    [MPEG_AUDIO_VERSION_2_5][MPEG_AUDIO_LAYER_1] = 384,
+    [MPEG_AUDIO_VERSION_2_5][MPEG_AUDIO_LAYER_2] = 1152,
+    [MPEG_AUDIO_VERSION_2_5][MPEG_AUDIO_LAYER_3] = 576,
+};
+
 enum ID3Encodings {
     ID3_ENCODING_LATIN1 = 0,
     ID3_ENCODING_UTF16,
@@ -95,7 +298,8 @@ struct plugin {
 static const char _name[] = "id3";
 static const struct lms_string_size _exts[] = {
     LMS_STATIC_STRING_SIZE(".mp3"),
-    LMS_STATIC_STRING_SIZE(".aac")
+    LMS_STATIC_STRING_SIZE(".aac"),
+    LMS_STATIC_STRING_SIZE(".adts"),
 };
 static const char *_cats[] = {
     "multimedia",
@@ -122,6 +326,20 @@ _to_uint(const char *data, int data_size)
     return sum;
 }
 
+static unsigned int
+_to_uint_max7b(const char *data, int data_size)
+{
+    unsigned int sum = 0;
+    unsigned int last, i;
+
+    last = data_size > 4 ? 3 : data_size - 1;
+
+    for (i = 0; i <= last; i++)
+        sum |= ((unsigned char) data[i]) << ((last - i) * 7);
+
+    return sum;
+}
+
 static inline int
 _is_id3v2_second_synch_byte(unsigned char byte)
 {
@@ -132,8 +350,315 @@ _is_id3v2_second_synch_byte(unsigned char byte)
     return 0;
 }
 
+static inline int
+_fill_mp3_header(struct mpeg_header *hdr, const uint8_t b[4])
+{
+    unsigned int bitrate_idx = (b[2] & 0xF0) >> 4;
+
+    hdr->sampling_rate_idx = (b[2] & 0x0C) >> 2;
+
+    if (hdr->sampling_rate_idx == 0x3)
+        return -1;
+    /*
+     * Sampling rate frequency index
+     * bits     MPEG1           MPEG2           MPEG2.5
+     * 00       44100 Hz        22050 Hz        11025 Hz
+     * 01       48000 Hz        24000 Hz        12000 Hz
+     * 10       32000 Hz        16000 Hz        8000 Hz
+     * 11       reserv.         reserv.         reserv.
+     */
+
+    /* swap 0x1 and 0x0 */
+    if (hdr->sampling_rate_idx < 0x2)
+        hdr->sampling_rate_idx = !hdr->sampling_rate_idx;
+    hdr->sampling_rate_idx += 3 * hdr->version + 3;
+
+    hdr->codec_idx = hdr->version * 3 + hdr->layer;
+
+    hdr->channels = (b[3] & 0xC0) >> 6;
+    hdr->channels = hdr->channels == 0x3 ? 1 : 2;
+
+    hdr->bitrate_idx = bitrate_idx;
+
+    return 0;
+}
+
+static inline int
+_fill_aac_header(struct mpeg_header *hdr, const uint8_t b[4])
+{
+    unsigned int profile;
+
+    hdr->sampling_rate_idx = (b[2] & 0x3C) >> 2;
+
+    profile = (b[2] & 0xC0) >> 6;
+    hdr->codec_idx = MPEG_CODEC_AAC_START + profile;
+    if (hdr->version == MPEG_AUDIO_VERSION_4)
+        hdr->codec_idx += 4;
+
+    hdr->channels = ((b[2] & 0x1) << 2) | ((b[3] & 0xC0) >> 6);
+    return 0;
+}
+
+static inline int
+_fill_mpeg_header(struct mpeg_header *hdr, const uint8_t b[4])
+{
+    unsigned int version = (b[1] & 0x18) >>  3;
+    unsigned int layer = (b[1] & 0x06) >> 1;
+
+    switch (layer) {
+    case 0x0:
+        if (version == 0x2 || version == 0x3)
+            hdr->layer = MPEG_AUDIO_LAYER_AAC;
+        else
+            return -1;
+        break;
+    case 0x1:
+        hdr->layer = MPEG_AUDIO_LAYER_3;
+        break;
+    case 0x2:
+        hdr->layer = MPEG_AUDIO_LAYER_2;
+        break;
+    case 0x3:
+        hdr->layer = MPEG_AUDIO_LAYER_1;
+        break;
+    }
+
+    switch (version) {
+    case 0x0:
+        hdr->version = MPEG_AUDIO_VERSION_2_5;
+        break;
+    case 0x1:
+        return -1;
+    case 0x2:
+        if (layer == 0x0)
+            hdr->version = MPEG_AUDIO_VERSION_4;
+        else
+            hdr->version = MPEG_AUDIO_VERSION_2;
+        break;
+    case 0x3:
+        if (layer == 0x0)
+            hdr->version = MPEG_AUDIO_VERSION_2;
+        else
+            hdr->version = MPEG_AUDIO_VERSION_1;
+    }
+
+    hdr->crc = !(b[1] & 0x1);
+    hdr->padding = b[2] & 0x2;
+
+    return 0;
+}
+
+static int
+_estimate_mp3_bitrate_from_frames(int fd, off_t mpeg_offset,
+                                  struct mpeg_header *orig_hdr)
+{
+    struct mpeg_header hdr = *orig_hdr;
+    off_t offset = mpeg_offset;
+    unsigned int sum = 0, i;
+    int r;
+    bool cbr = true;
+    /* For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8
+     * bits long.
+     * [layer == 1] */
+    unsigned int padding_size_table[2] = { 1, 4 };
+    unsigned int samples_per_frame, sampling_rate;
+
+    samples_per_frame = _samples_per_frame_table[hdr.version][hdr.layer];
+    sampling_rate = _sample_rates[hdr.sampling_rate_idx];
+    assert(sampling_rate != 0);
+
+    for (i = 0; i < N_FRAMES_BITRATE_ESTIMATE;) {
+        unsigned int bitrate, padding_size;
+        unsigned int framesize;
+        uint8_t buf[4];
+
+        bitrate = _bitrate_table[hdr.version][hdr.layer][hdr.bitrate_idx];
+        if (cbr && bitrate == hdr.bitrate && i > N_FRAMES_CBR_ESTIMATE) {
+            i = 1;
+            sum = bitrate;
+            break;
+        }
+
+        sum += bitrate;
+        i++;
+
+        padding_size = hdr.padding ? padding_size_table[hdr.layer == 1] : 0;
+
+        framesize = 4;          /* mpeg header */
+        framesize += (samples_per_frame / 8) * bitrate * 1000;
+        framesize /= sampling_rate;
+        framesize += (padding_size * samples_per_frame);
+
+        offset += framesize;
+
+        lseek(fd, offset, SEEK_SET);
+        r = read(fd, buf, sizeof(buf));
+
+        if (r < 0) {
+            fprintf(stderr, "ERROR reading frame header at %#x\n",
+                    (unsigned int) offset);
+            break;
+        }
+        if (!r)
+            break;
+
+        if (buf[0] != 0xff || !_is_id3v2_second_synch_byte(buf[1]) ||
+            _fill_mpeg_header(&hdr, buf) < 0 ||
+            _fill_mp3_header(&hdr, buf) < 0) {
+            fprintf(stderr, "ERROR interpreting frame header at 0x%x:"
+                    "%#02x %#02x %#02x %#02x\n", (unsigned int) offset,
+                    buf[0], buf[1], buf[2], buf[3]);
+            break;
+        }
+    }
+
+    orig_hdr->bitrate = sum / i * 1000;
+
+    return 0;
+}
+
+static int
+_parse_vbr_headers(int fd, off_t mpeg_offset, struct mpeg_header *hdr)
+{
+    unsigned int sampling_rate, samples_per_frame, flags, nframes = 0, size = 0;
+    int xing_offset_table[2][2] = { /* [(version == 1)][channels == 1)] */
+        { 17,  9 },
+        { 32, 17 }
+    };
+    uint8_t buf[18];
+    off_t xing_offset;
+
+    /* Try Xing first since it's the most likely to be there */
+    xing_offset = mpeg_offset + 4 + 2 * hdr->crc
+        + xing_offset_table[(hdr->version == 1)][(hdr->channels == 1)];
+
+    lseek(fd, xing_offset, SEEK_SET);
+    if (read(fd, buf, sizeof(buf)) != sizeof(buf))
+        return -1;
+
+    hdr->cbr = (memcmp(buf, "Info", 4) == 0);
+    if (hdr->cbr || memcmp(buf, "Xing", 4) == 0) {
+        flags = buf[7];
+
+        if (flags & 1)
+            nframes = get_be32(&buf[8]);
+        if (flags & 2)
+            size = get_be32(&buf[8 + !!(flags & 1) * 4]);
+
+        goto proceed;
+    }
+
+    /* VBRI is found in files encoded by Fraunhofer Encoder. Fixed location: 32
+     * bytes after the mpeg header */
+    lseek(fd, mpeg_offset + 36, SEEK_SET);
+    if (read(fd, buf, sizeof(buf)) != sizeof(buf))
+        return -1;
+
+    if (memcmp(buf, "VBRI", 4) == 0 && get_be16(buf) == 1) {
+        size = get_be32(&buf[10]);
+        nframes = get_be32(&buf[14]);
+
+        goto proceed;
+    }
+
+    return 0;
+
+proceed:
+    samples_per_frame = _samples_per_frame_table[hdr->version][hdr->layer];
+    sampling_rate = _sample_rates[hdr->sampling_rate_idx];
+    assert(sampling_rate != 0);
+
+    hdr->length = (nframes * samples_per_frame) / sampling_rate;
+
+    if (hdr->length)
+        hdr->bitrate = (8 * size) / (1000 * hdr->length);
+
+    return 0;
+}
+
+static int
+_parse_mpeg_header(int fd, off_t off, struct lms_audio_info *audio_info,
+                   size_t size)
+{
+    uint8_t buffer[32];
+    const uint8_t *p, *p_end;
+    unsigned int prev_read;
+    struct mpeg_header hdr = { };
+    int r;
+
+    lseek(fd, off, SEEK_SET);
+
+    /* Find sync word */
+    prev_read = 0;
+    do {
+        int nread = read(fd, buffer + prev_read, sizeof(buffer) - prev_read);
+        if (nread < MPEG_HEADER_SIZE)
+            return -1;
+
+        p = buffer;
+        p_end = buffer + nread;
+        off += nread - prev_read;
+        while (p < p_end && (p = memchr(p, 0xFF, p_end - p))) {
+            /* poor man's ring buffer since the needle is small (4 bytes) */
+            if (p > p_end - MPEG_HEADER_SIZE) {
+                memcpy(buffer, p, p_end - p);
+                break;
+            }
+
+            if (_is_id3v2_second_synch_byte(*(p + 1))) {
+                off -= (p_end - p);
+                goto found;
+            }
+
+            p++;
+        }
+        prev_read = p ? p_end - p : 0;
+    } while(1);
+
+found:
+    if (_fill_mpeg_header(&hdr, p) < 0) {
+        fprintf(stderr, "Invalid field in file, ignoring.\n");
+        return 0;
+    }
+
+    if (hdr.layer == MPEG_AUDIO_LAYER_AAC)
+        r = _fill_aac_header(&hdr, p);
+    else {
+        if ((r = _fill_mp3_header(&hdr, p) < 0) ||
+            (r = _parse_vbr_headers(fd, off, &hdr) < 0))
+            return r;
+
+        if (hdr.cbr)
+            hdr.bitrate =
+                _bitrate_table[hdr.version][hdr.layer][hdr.bitrate_idx] * 1000;
+        else if (!hdr.bitrate) {
+            r = _estimate_mp3_bitrate_from_frames(fd, off, &hdr);
+            if (r < 0)
+                return r;
+        }
+
+        if (!hdr.length)
+            hdr.length =  (8 * (size - off)) / (1000 * hdr.bitrate);
+    }
+
+    if (r < 0)
+        return r;
+
+    audio_info->codec = *_codecs[hdr.codec_idx];
+    audio_info->sampling_rate = _sample_rates[hdr.sampling_rate_idx];
+    audio_info->channels = hdr.channels;
+    audio_info->bitrate = hdr.bitrate;
+    audio_info->length = hdr.length;
+
+    return 0;
+}
+
+/* Returns the offset in fd to the position after the ID3 tag, iff it occurs
+ * *before* a sync word. Otherwise < 0 is returned and if we gave up looking
+ * after ID3 because of a sync value, @syncframe_offset is set to its
+ * correspondent offset */
 static long
-_find_id3v2(int fd)
+_find_id3v2(int fd, off_t *sync_offset)
 {
     static const char pattern[3] = "ID3";
     char buffer[3];
@@ -166,8 +691,10 @@ _find_id3v2(int fd)
 
         /* (1) previous partial match */
         if (prev_part_match_sync) {
-            if (_is_id3v2_second_synch_byte(buffer[0]))
+            if (_is_id3v2_second_synch_byte(buffer[0])) {
+                *sync_offset = buffer_offset - 1;
                 return -1;
+            }
             prev_part_match_sync = 0;
         }
 
@@ -206,9 +733,11 @@ _find_id3v2(int fd)
 
                 q = p + 1;
                 if (q < p_end) {
-                    if (_is_id3v2_second_synch_byte(*q))
+                    if (_is_id3v2_second_synch_byte(*q)) {
                         /* (2) synch pattern contained in current buffer */
+                        *sync_offset = buffer_offset + (p - buffer);
                         return -1;
+                    }
                 } else
                     /* (3) partial match */
                     prev_part_match_sync = 1;
@@ -299,12 +828,11 @@ _get_id3v2_artist(unsigned int index, const char *frame_data, unsigned int frame
         return 1;
 
     if (artist_priorities[index] > info->cur_artist_priority) {
-        struct lms_string_size artist = {0};
+        struct lms_string_size artist = { };
 
         _get_id3v2_frame_info(frame_data, frame_size, &artist, cs_conv, 1);
         if (artist.str) {
-            if (info->artist.str)
-                free(info->artist.str);
+            free(info->artist.str);
             info->artist = artist;
             info->cur_artist_priority = artist_priorities[index];
         }
@@ -346,8 +874,8 @@ _parse_id3v1_genre(const char *str_genre, struct lms_string_size *out)
 static void
 _get_id3v2_genre(const char *frame_data, unsigned int frame_size, struct lms_string_size *out, lms_charset_conv_t *cs_conv)
 {
-    int i, is_number;
-    struct lms_string_size genre = {0};
+    unsigned int i, is_number;
+    struct lms_string_size genre = { };
 
     _get_id3v2_frame_info(frame_data, frame_size, &genre, cs_conv, 1);
     if (!genre.str)
@@ -413,7 +941,7 @@ _get_id3v2_genre(const char *frame_data, unsigned int frame_size, struct lms_str
 static void
 _get_id3v2_trackno(const char *frame_data, unsigned int frame_size, struct id3_info *info, lms_charset_conv_t *cs_conv)
 {
-    struct lms_string_size trackno = {0};
+    struct lms_string_size trackno = { };
 
     _get_id3v2_frame_info(frame_data, frame_size, &trackno, cs_conv, 0);
     if (!trackno.str)
@@ -496,7 +1024,8 @@ _parse_id3v2_frame(struct id3v2_frame_header *fh, const char *frame_data, struct
 }
 
 static int
-_parse_id3v2(int fd, long id3v2_offset, struct id3_info *info, lms_charset_conv_t **cs_convs)
+_parse_id3v2(int fd, long id3v2_offset, struct id3_info *info,
+             lms_charset_conv_t **cs_convs, off_t *ptag_size)
 {
     char header_data[10], frame_header_data[10];
     unsigned int tag_size, major_version, frame_data_pos, frame_data_length, frame_header_size;
@@ -510,10 +1039,12 @@ _parse_id3v2(int fd, long id3v2_offset, struct id3_info *info, lms_charset_conv_
     if (read(fd, header_data, ID3V2_HEADER_SIZE) != ID3V2_HEADER_SIZE)
         return -1;
 
-    tag_size = _to_uint(header_data + 6, 4);
+    tag_size = _to_uint_max7b(header_data + 6, 4);
     if (tag_size == 0)
         return -1;
 
+    *ptag_size = tag_size + ID3V2_HEADER_SIZE;
+
     /* parse frames */
     major_version = header_data[3];
 
@@ -525,12 +1056,18 @@ _parse_id3v2(int fd, long id3v2_offset, struct id3_info *info, lms_charset_conv_
     if (extended_header) {
         /* skip extended header */
         unsigned int extended_header_size;
-        char extended_header_data[4];
+        char extended_header_data[6];
+        bool crc;
 
         if (read(fd, extended_header_data, 4) != 4)
             return -1;
+
         extended_header_size = _to_uint(extended_header_data, 4);
-        lseek(fd, extended_header_size - 4, SEEK_CUR);
+        crc = extended_header_data[5] & 0x8000;
+
+        *ptag_size += extended_header_size + (crc * 4);
+
+        lseek(fd, extended_header_size - 6, SEEK_CUR);
         frame_data_pos += extended_header_size;
         frame_data_length -= extended_header_size;
     }
@@ -608,7 +1145,7 @@ _id3v1_str_get(struct lms_string_size *s, const char *buf, int maxlen, lms_chars
         return;
 
     len++; /* p_last is not included yet */
-    if (len > s->len) {
+    if ((unsigned)len > s->len) {
         char *tmp;
 
         tmp = realloc(s->str, sizeof(char) * (len + 1));
@@ -669,6 +1206,7 @@ _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_in
     struct lms_audio_info audio_info = { };
     int r, fd;
     long id3v2_offset;
+    off_t sync_offset = 0;
 
     fd = open(finfo->path, O_RDONLY);
     if (fd < 0) {
@@ -676,13 +1214,18 @@ _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_in
         return -1;
     }
 
-    id3v2_offset = _find_id3v2(fd);
+    id3v2_offset = _find_id3v2(fd, &sync_offset);
     if (id3v2_offset >= 0) {
+        off_t id3v2_size = 3;
+
+        sync_offset = id3v2_offset;
+
 #if 0
         fprintf(stderr, "id3v2 tag found in file %s with offset %ld\n",
                 finfo->path, id3v2_offset);
 #endif
-        if (_parse_id3v2(fd, id3v2_offset, &info, plugin->cs_convs) != 0 ||
+        if (_parse_id3v2(fd, id3v2_offset, &info, plugin->cs_convs,
+                         &id3v2_size) != 0 ||
             !info.title.str || !info.artist.str ||
             !info.album.str || !info.genre.str ||
             info.trackno == -1) {
@@ -691,6 +1234,10 @@ _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_in
 #endif
             id3v2_offset = -1;
         }
+
+        /* Even if we later failed to parse the ID3, we want to look for sync
+         * frame only after the tag */
+        sync_offset += id3v2_size;
     }
 
     if (id3v2_offset < 0) {
@@ -721,15 +1268,11 @@ _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_in
         }
     }
 
-    if (!info.title.str) {
-        long ext_idx;
-        ext_idx = ((long)match) - 1;
-        info.title.len = finfo->path_len - finfo->base - _exts[ext_idx].len;
-        info.title.str = malloc((info.title.len + 1) * sizeof(char));
-        memcpy(info.title.str, finfo->path + finfo->base, info.title.len);
-        info.title.str[info.title.len] = '\0';
-        lms_charset_conv(ctxt->cs_conv, &info.title.str, &info.title.len);
-    }
+    if (!info.title.str)
+        info.title = str_extract_name_from_path(finfo->path, finfo->path_len,
+                                                finfo->base,
+                                                &_exts[((long) match) - 1],
+                                                ctxt->cs_conv);
 
     if (info.trackno == -1)
         info.trackno = 0;
@@ -749,20 +1292,21 @@ _parse(struct plugin *plugin, struct lms_context *ctxt, const struct lms_file_in
     audio_info.album = info.album;
     audio_info.genre = info.genre;
     audio_info.trackno = info.trackno;
+
+    _parse_mpeg_header(fd, sync_offset, &audio_info, finfo->size);
+
+    _fill_dlna_profile(&audio_info);
+
     r = lms_db_audio_add(plugin->audio_db, &audio_info);
 
   done:
     posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED);
     close(fd);
 
-    if (info.title.str)
-        free(info.title.str);
-    if (info.artist.str)
-        free(info.artist.str);
-    if (info.album.str)
-        free(info.album.str);
-    if (info.genre.str)
-        free(info.genre.str);
+    free(info.title.str);
+    free(info.artist.str);
+    free(info.album.str);
+    free(info.genre.str);
 
     return r;
 }
@@ -844,7 +1388,7 @@ lms_plugin_open(void)
     return (struct lms_plugin *)plugin;
 }
 
-API struct lms_plugin_info *
+API const struct lms_plugin_info *
 lms_plugin_info(void)
 {
     static struct lms_plugin_info info = {