From taglib-devel Mon Jan 29 20:33:29 2007 From: "Xavier Duret" Date: Mon, 29 Jan 2007 20:33:29 +0000 To: taglib-devel Subject: [PATCH] Add support for TLEN frames and add ReadStyle check before Message-Id: X-MARC-Message: https://marc.info/?l=taglib-devel&m=117010281513460 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); _______________________________________________ taglib-devel mailing list taglib-devel@kde.org https://mail.kde.org/mailman/listinfo/taglib-devel