[PATCH] Add support for TLEN frames and add ReadStyle check before brute forcing

Xavier Duret xaviour.maillists at gmail.com
Mon Jan 29 21:33:29 CET 2007


This patch adds:
- Using TLEN frames when available and when there is no Xing header.
- Checking ReadStyle before attempting to brute force a VBR file
without Xing header as Scott suggested.

I am not very satisfied with this patch. The reference to the ID3v2
tag is quite ugly. Moreover my testing show that there is at least one
encoder/tagger out there that write invalid TLEN frames (song duration
divided by ten).


diff -ruN taglib.old/taglib/mpeg/mpegproperties.cpp
taglib/taglib/mpeg/mpegproperties.cpp
--- taglib.old/taglib/mpeg/mpegproperties.cpp   2007-01-29
16:06:34.000000000 +0100
+++ taglib/taglib/mpeg/mpegproperties.cpp       2007-01-29
19:33:42.000000000 +0100
@@ -25,6 +25,7 @@
 #include "mpegproperties.h"
 #include "mpegfile.h"
 #include "xingheader.h"
+#include "id3v2tag.h"

 using namespace TagLib;

@@ -198,7 +199,7 @@

   // Check for a Xing header that will help us in gathering information about a
   // VBR stream.
-
+  ID3v2::Tag *fileTag = d->file->ID3v2Tag();
   int xingHeaderOffset =
MPEG::XingHeader::xingHeaderOffset(firstHeader.version(),

firstHeader.channelMode());

@@ -214,57 +215,72 @@
       d->length = (firstHeader.samplesPerFrame() *
d->xingHeader->totalFrames()) / firstHeader.sampleRate();
       d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 /
d->length / 1000 : 0;
   }
-  else {
-    // Since there was no valid Xing header found, we hope that we're
in a constant
-    // bitrate file.
+  else if (fileTag && fileTag->trackLength()) {
+    delete d->xingHeader;
+    d->xingHeader = 0;

+    d->length = fileTag->trackLength() / 1000;
+    d->bitrate = d->length > 0 ? (last - first) / d->length / 1000 : 0;
+  } else {
+    bool assumeCbr;
     delete d->xingHeader;
     d->xingHeader = 0;

-    // TODO: Make this more robust with audio property detection for
VBR without a
-    // Xing header.
-    long position = first;
-    uint nSamples = 0;
-    uint nFrames = 0;
-    int bitRate = 0;
-    bool cbr = true;
-    d->file->seek(position);
-    while (position < d->file->length()) {
-      Header currentHeader(d->file->readBlock(4));
-      if (currentHeader.isValid()) {
-        position += currentHeader.frameLength();
-        nSamples += (currentHeader.layer() > 1) ? 1152 : 384;
-        d->file->seek(position);
-
-        // Assume that if a file has a constant bit rate for more
than 100 frames
-        // then it is CBR and there is no point in parsing it complely.
-        if (bitRate == 0)
-          bitRate = currentHeader.bitrate();
-        else {
-          if (cbr && (bitRate != currentHeader.bitrate())) {
-            cbr = false;
+    if (d->style != Accurate)
+      // Since there was no valid Xing header found, we hope that
we're in a constant
+      // bitrate file.
+      assumeCbr = true;
+    else {
+      // Scan through the entire file to calculate the track length
and the average
+      // bitrate.
+      long position = first;
+      int maxPosition;
+      uint nSamples = 0;
+      uint nFrames = 0;
+      int bitRate = 0;
+      bool cbr = true;
+
+      d->file->seek(position);
+      maxPosition = d->file->length();
+
+      while (position < maxPosition) {
+        Header currentHeader(d->file->readBlock(4));
+        if (currentHeader.isValid()) {
+          position += currentHeader.frameLength();
+          nSamples += (currentHeader.layer() > 1) ? 1152 : 384;
+          d->file->seek(position);
+
+          // Assume that if a file has a constant bit rate for more
than 100 frames
+          // then it is CBR and there is no point in parsing it complely.
+          if (bitRate == 0)
             bitRate = currentHeader.bitrate();
+          else {
+            if (cbr && (bitRate != currentHeader.bitrate())) {
+              cbr = false;
+              bitRate = currentHeader.bitrate();
+            }
           }
-        }
-        nFrames += 1;
-        if (cbr && nFrames > 100)
+          nFrames += 1;
+          if (cbr && nFrames > 100)
+            break;
+
+        } else
           break;
+      }

+      if (position >= maxPosition) {
+        d->length = nSamples / firstHeader.sampleRate();
+        d->bitrate = d->length > 0 ? (position - first) / d->length / 1000 : 0;
       } else
-        break;
+        assumeCbr = true;
     }

-    if (position >= d->file->length()) {
-      d->length = nSamples / firstHeader.sampleRate();
-      d->bitrate = d->length > 0 ? (position - first) / d->length / 1000 : 0;
-    } else {
-      if(firstHeader.frameLength() > 0 && firstHeader.bitrate() > 0) {
-        int frames = (last - first) / firstHeader.frameLength() + 1;
-
-        d->length = int(float(firstHeader.frameLength() * frames) /
-                        float(firstHeader.bitrate() * 125) + 0.5);
-        d->bitrate = firstHeader.bitrate();
-      }
+    if(assumeCbr && firstHeader.frameLength() > 0 &&
firstHeader.bitrate() > 0) {
+      int frames = (last - first) / firstHeader.frameLength() + 1;
+
+      d->length = int(float(firstHeader.frameLength() * frames) /
+                      float(firstHeader.bitrate() * 125) + 0.5);
+      d->bitrate = firstHeader.bitrate();
     }
   }

diff -ruN taglib.old/taglib/mpeg/id3v2/id3v2tag.h
taglib/taglib/mpeg/id3v2/id3v2tag.h
--- taglib.old/taglib/mpeg/id3v2/id3v2tag.h     2007-01-17
16:01:33.000000000 +0100
+++ taglib/taglib/mpeg/id3v2/id3v2tag.h 2007-01-29 19:23:33.000000000 +0100
@@ -148,6 +148,12 @@
       virtual bool isEmpty() const;

       /*!
+       * Returns the tracks length in ms if specified in the tag
+       * 0 otherwise.
+       */
+      uint trackLength() const;
+
+      /*!
        * Returns a pointer to the tag's header.
        */
       Header *header() const;

diff -ruN taglib.old/taglib/mpeg/id3v2/id3v2tag.cpp
taglib/taglib/mpeg/id3v2/id3v2tag.cpp
--- taglib.old/taglib/mpeg/id3v2/id3v2tag.cpp   2007-01-17
16:01:33.000000000 +0100
+++ taglib/taglib/mpeg/id3v2/id3v2tag.cpp       2007-01-29
19:40:54.000000000 +0100
@@ -196,6 +196,13 @@
   return 0;
 }

+TagLib::uint ID3v2::Tag::trackLength() const
+{
+  if(!d->frameListMap["TLEN"].isEmpty())
+    return d->frameListMap["TLEN"].front()->toString().toInt();
+  return 0;
+}
+
 void ID3v2::Tag::setTitle(const String &s)
 {
   setTextFrame("TIT2", s);


More information about the taglib-devel mailing list