1 /***************************************************************************
2 copyright : (C) 2002 - 2008 by Scott Wheeler
3 email : wheeler@kde.org
4 ***************************************************************************/
5
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. *
10 * *
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. *
15 * *
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., 51 Franklin Street, Fifth Floor, Boston, MA *
19 * 02110-1301 USA *
20 * *
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 ***************************************************************************/
25
26 #include <tdebug.h>
27 #include <tstring.h>
28
29 #include "mpegproperties.h"
30 #include "mpegfile.h"
31 #include "xingheader.h"
32 #include "apetag.h"
33 #include "apefooter.h"
34
35 using namespace TagLib;
36
37 class MPEG::Properties::PropertiesPrivate
38 {
39 public:
PropertiesPrivate()40 PropertiesPrivate() :
41 xingHeader(0),
42 length(0),
43 bitrate(0),
44 sampleRate(0),
45 channels(0),
46 layer(0),
47 version(Header::Version1),
48 channelMode(Header::Stereo),
49 protectionEnabled(false),
50 isCopyrighted(false),
51 isOriginal(false) {}
52
~PropertiesPrivate()53 ~PropertiesPrivate()
54 {
55 delete xingHeader;
56 }
57
58 XingHeader *xingHeader;
59 int length;
60 int bitrate;
61 int sampleRate;
62 int channels;
63 int layer;
64 Header::Version version;
65 Header::ChannelMode channelMode;
66 bool protectionEnabled;
67 bool isCopyrighted;
68 bool isOriginal;
69 };
70
71 ////////////////////////////////////////////////////////////////////////////////
72 // public members
73 ////////////////////////////////////////////////////////////////////////////////
74
Properties(File * file,ReadStyle style)75 MPEG::Properties::Properties(File *file, ReadStyle style) :
76 AudioProperties(style),
77 d(new PropertiesPrivate())
78 {
79 read(file);
80 }
81
~Properties()82 MPEG::Properties::~Properties()
83 {
84 delete d;
85 }
86
length() const87 int MPEG::Properties::length() const
88 {
89 return lengthInSeconds();
90 }
91
lengthInSeconds() const92 int MPEG::Properties::lengthInSeconds() const
93 {
94 return d->length / 1000;
95 }
96
lengthInMilliseconds() const97 int MPEG::Properties::lengthInMilliseconds() const
98 {
99 return d->length;
100 }
101
bitrate() const102 int MPEG::Properties::bitrate() const
103 {
104 return d->bitrate;
105 }
106
sampleRate() const107 int MPEG::Properties::sampleRate() const
108 {
109 return d->sampleRate;
110 }
111
channels() const112 int MPEG::Properties::channels() const
113 {
114 return d->channels;
115 }
116
xingHeader() const117 const MPEG::XingHeader *MPEG::Properties::xingHeader() const
118 {
119 return d->xingHeader;
120 }
121
version() const122 MPEG::Header::Version MPEG::Properties::version() const
123 {
124 return d->version;
125 }
126
layer() const127 int MPEG::Properties::layer() const
128 {
129 return d->layer;
130 }
131
protectionEnabled() const132 bool MPEG::Properties::protectionEnabled() const
133 {
134 return d->protectionEnabled;
135 }
136
channelMode() const137 MPEG::Header::ChannelMode MPEG::Properties::channelMode() const
138 {
139 return d->channelMode;
140 }
141
isCopyrighted() const142 bool MPEG::Properties::isCopyrighted() const
143 {
144 return d->isCopyrighted;
145 }
146
isOriginal() const147 bool MPEG::Properties::isOriginal() const
148 {
149 return d->isOriginal;
150 }
151
152 ////////////////////////////////////////////////////////////////////////////////
153 // private members
154 ////////////////////////////////////////////////////////////////////////////////
155
read(File * file)156 void MPEG::Properties::read(File *file)
157 {
158 // Only the first valid frame is required if we have a VBR header.
159
160 const long firstFrameOffset = file->firstFrameOffset();
161 if(firstFrameOffset < 0) {
162 debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream.");
163 return;
164 }
165
166 const Header firstHeader(file, firstFrameOffset, false);
167
168 // Check for a VBR header that will help us in gathering information about a
169 // VBR stream.
170
171 file->seek(firstFrameOffset);
172 d->xingHeader = new XingHeader(file->readBlock(firstHeader.frameLength()));
173 if(!d->xingHeader->isValid()) {
174 delete d->xingHeader;
175 d->xingHeader = 0;
176 }
177
178 if(d->xingHeader && firstHeader.samplesPerFrame() > 0 && firstHeader.sampleRate() > 0) {
179
180 // Read the length and the bitrate from the VBR header.
181
182 const double timePerFrame = firstHeader.samplesPerFrame() * 1000.0 / firstHeader.sampleRate();
183 const double length = timePerFrame * d->xingHeader->totalFrames();
184
185 d->length = static_cast<int>(length + 0.5);
186 d->bitrate = static_cast<int>(d->xingHeader->totalSize() * 8.0 / length + 0.5);
187 }
188 else if(firstHeader.bitrate() > 0) {
189
190 // Since there was no valid VBR header found, we hope that we're in a constant
191 // bitrate file.
192
193 // TODO: Make this more robust with audio property detection for VBR without a
194 // Xing header.
195
196 d->bitrate = firstHeader.bitrate();
197
198 // Look for the last MPEG audio frame to calculate the stream length.
199
200 const long lastFrameOffset = file->lastFrameOffset();
201 if(lastFrameOffset < 0) {
202 debug("MPEG::Properties::read() -- Could not find an MPEG frame in the stream.");
203 return;
204 }
205
206 const Header lastHeader(file, lastFrameOffset, false);
207 const long streamLength = lastFrameOffset - firstFrameOffset + lastHeader.frameLength();
208 if(streamLength > 0)
209 d->length = static_cast<int>(streamLength * 8.0 / d->bitrate + 0.5);
210 }
211
212 d->sampleRate = firstHeader.sampleRate();
213 d->channels = firstHeader.channelMode() == Header::SingleChannel ? 1 : 2;
214 d->version = firstHeader.version();
215 d->layer = firstHeader.layer();
216 d->protectionEnabled = firstHeader.protectionEnabled();
217 d->channelMode = firstHeader.channelMode();
218 d->isCopyrighted = firstHeader.isCopyrighted();
219 d->isOriginal = firstHeader.isOriginal();
220 }
221