1 /***************************************************************************
2 copyright : (C) 2002 - 2008 by Scott Wheeler
3 email : wheeler@kde.org
4 ***************************************************************************/
6 /***************************************************************************
7 * This library is free software; you can redistribute it and/or modify *
8 * it under the terms of the GNU Lesser General Public License version *
9 * 2.1 as published by the Free Software Foundation. *
11 * This library is distributed in the hope that it will be useful, but *
12 * WITHOUT ANY WARRANTY; without even the implied warranty of *
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU *
14 * Lesser General Public License for more details. *
16 * You should have received a copy of the GNU Lesser General Public *
17 * License along with this library; if not, write to the Free Software *
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 *
21 * Alternatively, this file is available under the Mozilla Public *
22 * License Version 1.1. You may obtain a copy of the License at *
23 * http://www.mozilla.org/MPL/ *
24 ***************************************************************************/
29 #include "mpegproperties.h"
31 #include "xingheader.h"
33 using namespace TagLib;
35 class MPEG::Properties::PropertiesPrivate
38 PropertiesPrivate(File *f, ReadStyle s) :
47 version(Header::Version1),
48 channelMode(Header::Stereo),
49 protectionEnabled(false),
59 XingHeader *xingHeader;
66 Header::Version version;
67 Header::ChannelMode channelMode;
68 bool protectionEnabled;
73 ////////////////////////////////////////////////////////////////////////////////
75 ////////////////////////////////////////////////////////////////////////////////
77 MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
79 d = new PropertiesPrivate(file, style);
81 if(file && file->isOpen())
85 MPEG::Properties::~Properties()
90 int MPEG::Properties::length() const
95 int MPEG::Properties::bitrate() const
100 int MPEG::Properties::sampleRate() const
102 return d->sampleRate;
105 int MPEG::Properties::channels() const
110 const MPEG::XingHeader *MPEG::Properties::xingHeader() const
112 return d->xingHeader;
115 MPEG::Header::Version MPEG::Properties::version() const
120 int MPEG::Properties::layer() const
125 bool MPEG::Properties::protectionEnabled() const
127 return d->protectionEnabled;
130 MPEG::Header::ChannelMode MPEG::Properties::channelMode() const
132 return d->channelMode;
135 bool MPEG::Properties::isCopyrighted() const
137 return d->isCopyrighted;
140 bool MPEG::Properties::isOriginal() const
142 return d->isOriginal;
145 ////////////////////////////////////////////////////////////////////////////////
147 ////////////////////////////////////////////////////////////////////////////////
149 void MPEG::Properties::read()
151 // Since we've likely just looked for the ID3v1 tag, start at the end of the
152 // file where we're least likely to have to have to move the disk head.
154 long last = d->file->lastFrameOffset();
157 debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.");
162 Header lastHeader(d->file->readBlock(4));
164 long first = d->file->firstFrameOffset();
167 debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream.");
171 if(!lastHeader.isValid()) {
177 pos = d->file->previousFrameOffset(pos);
183 Header header(d->file->readBlock(4));
185 if(header.isValid()) {
193 // Now jump back to the front of the file and read what we need from there.
195 d->file->seek(first);
196 Header firstHeader(d->file->readBlock(4));
198 if(!firstHeader.isValid() || !lastHeader.isValid()) {
199 debug("MPEG::Properties::read() -- Page headers were invalid.");
203 // Check for a Xing header that will help us in gathering information about a
206 int xingHeaderOffset = MPEG::XingHeader::xingHeaderOffset(firstHeader.version(),
207 firstHeader.channelMode());
209 d->file->seek(first + xingHeaderOffset);
210 d->xingHeader = new XingHeader(d->file->readBlock(16));
212 // Read the length and the bitrate from the Xing header.
214 if(d->xingHeader->isValid() &&
215 firstHeader.sampleRate() > 0 &&
216 d->xingHeader->totalFrames() > 0)
218 double timePerFrame =
219 double(firstHeader.samplesPerFrame()) / firstHeader.sampleRate();
221 double length = timePerFrame * d->xingHeader->totalFrames();
223 d->length = int(length);
224 d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0;
227 // Since there was no valid Xing header found, we hope that we're in a constant
230 delete d->xingHeader;
233 // TODO: Make this more robust with audio property detection for VBR without a
236 if(firstHeader.frameLength() > 0 && firstHeader.bitrate() > 0) {
237 int frames = (last - first) / firstHeader.frameLength() + 1;
239 d->length = int(float(firstHeader.frameLength() * frames) /
240 float(firstHeader.bitrate() * 125) + 0.5);
241 d->bitrate = firstHeader.bitrate();
246 d->sampleRate = firstHeader.sampleRate();
247 d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2;
248 d->version = firstHeader.version();
249 d->layer = firstHeader.layer();
250 d->protectionEnabled = firstHeader.protectionEnabled();
251 d->channelMode = firstHeader.channelMode();
252 d->isCopyrighted = firstHeader.isCopyrighted();
253 d->isOriginal = firstHeader.isOriginal();