[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