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