[tracker-miners/wip/jfelder/mp3-compute-duration: 2/2] tracker-extract-mp3: Improve duration computation for VBR files



commit b0ac61c6f73aade56151dddcbe808cf42411bfc1
Author: Jean Felder <jfelder src gnome org>
Date:   Thu Dec 5 01:01:23 2019 +0100

    tracker-extract-mp3: Improve duration computation for VBR files
    
    When a file is encoded with variable bitrate mode (VBR), the bitrate
    of each frame can be different. In that case, It is necessary to know
    the average bitrate in order to compute the duration of the file. This
    calculation requires to loop through all the frames. This operation
    being too slow, Tracker loops only through the frames up to a
    threshold (MAX_FRAMES_SCAN) to approximate the average bitrate. This
    approximation can result in a computed duration being very different
    from the real duration.
    
    However, most of VBR files also have a XING header. This header can
    contain the total number of frames. By using the number of frames, it
    is possible to correctly compute the duration with the following
    formula:
    
    duration = number_of_frames * samples_per_frame / sampling_rate
    
    Closes: #88

 src/tracker-extract/tracker-extract-mp3.c | 55 +++++++++++++++++++++++++++++--
 1 file changed, 52 insertions(+), 3 deletions(-)
---
diff --git a/src/tracker-extract/tracker-extract-mp3.c b/src/tracker-extract/tracker-extract-mp3.c
index d49d32d54..0a88e2d98 100644
--- a/src/tracker-extract/tracker-extract-mp3.c
+++ b/src/tracker-extract/tracker-extract-mp3.c
@@ -950,6 +950,43 @@ get_id3 (const gchar *data,
        return TRUE;
 }
 
+static gboolean
+mp3_parse_xing_header (const gchar          *data,
+                       size_t                frame_pos,
+                       gchar                 mpeg_version,
+                       gint                  n_channels,
+                       guint32              *nr_frames)
+{
+       guint32 field_flags;
+       size_t pos;
+       guint xing_header_offset;
+
+       if (mpeg_version == MPEG_V1) {
+               xing_header_offset = (n_channels == 1) ? 21: 36;
+       } else {
+               xing_header_offset = (n_channels == 1) ? 13: 21;
+       }
+
+       pos = frame_pos + xing_header_offset;
+
+       /* header starts with "Xing" or "Info" */
+       if ((data[pos] == 0x58 && data[pos+1] == 0x69 && data[pos+2] == 0x6E && data[pos+3] == 0x67) ||
+           (data[pos] == 0x49 && data[pos+1] == 0x6E && data[pos+2] == 0x46 && data[pos+3] == 0x6F)) {
+               g_debug ("XING header found");
+       } else {
+               return FALSE;
+       }
+
+       /* Try to extract the number of frames if the frames field flag is set */
+       pos += 4;
+       field_flags = extract_uint32 (&data[pos]);
+       if ((field_flags & 0x0001) > 0) {
+               *nr_frames = extract_uint32 (&data[pos+4]);
+       }
+
+       return TRUE;
+}
+
 /*
  * For the MP3 frame header description, see
  * http://www.mp3-tech.org/programmer/frame_header.html
@@ -978,6 +1015,7 @@ mp3_parse_header (const gchar          *data,
        guint frames = 0;
        size_t pos = 0;
        gint n_channels;
+       guint32 xing_nr_frames = 0;
 
        pos = seek_pos;
 
@@ -1076,15 +1114,26 @@ mp3_parse_header (const gchar          *data,
                return FALSE;
        }
 
-       tracker_resource_set_string (resource, "nfo:codec", "MPEG");
-
        n_channels = ((header & ch_mask) == ch_mask) ? 1 : 2;
 
+       /* If the file is encoded in variable bit mode (VBR),
+          try to get the number of frames from the xing header
+          to compute the file duration.  */
+       if (vbr_flag) {
+               mp3_parse_xing_header (data, seek_pos, mpeg_ver, n_channels, &xing_nr_frames);
+       }
+
+       tracker_resource_set_string (resource, "nfo:codec", "MPEG");
+
        tracker_resource_set_int (resource, "nfo:channels", n_channels);
 
        avg_bps /= frames;
 
-       if ((!vbr_flag && frames > VBR_THRESHOLD) || (frames > MAX_FRAMES_SCAN)) {
+       if (vbr_flag && xing_nr_frames > 0) {
+               /* If the file is encoded with variable bitrate mode (VBR)
+                  and the number of frame is known */
+               length = spfp8 * 8 * xing_nr_frames / sample_rate;
+       } else if ((!vbr_flag && frames > VBR_THRESHOLD) || (frames > MAX_FRAMES_SCAN)) {
                /* If not all frames scanned
                 * Note that bitrate is always > 0, checked before */
                length = (filedata->size - filedata->id3v2_size) / (avg_bps ? avg_bps : (bitrate / 1000)) / 
125;


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]