Added TagLib (with AUTORS and COPYING files)
[someplayer] / src / taglib / mpeg / mpegproperties.cpp
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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  *
19  *   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
33 using namespace TagLib;
34
35 class MPEG::Properties::PropertiesPrivate
36 {
37 public:
38   PropertiesPrivate(File *f, ReadStyle s) :
39     file(f),
40     xingHeader(0),
41     style(s),
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
53   ~PropertiesPrivate()
54   {
55     delete xingHeader;
56   }
57
58   File *file;
59   XingHeader *xingHeader;
60   ReadStyle style;
61   int length;
62   int bitrate;
63   int sampleRate;
64   int channels;
65   int layer;
66   Header::Version version;
67   Header::ChannelMode channelMode;
68   bool protectionEnabled;
69   bool isCopyrighted;
70   bool isOriginal;
71 };
72
73 ////////////////////////////////////////////////////////////////////////////////
74 // public members
75 ////////////////////////////////////////////////////////////////////////////////
76
77 MPEG::Properties::Properties(File *file, ReadStyle style) : AudioProperties(style)
78 {
79   d = new PropertiesPrivate(file, style);
80
81   if(file && file->isOpen())
82     read();
83 }
84
85 MPEG::Properties::~Properties()
86 {
87   delete d;
88 }
89
90 int MPEG::Properties::length() const
91 {
92   return d->length;
93 }
94
95 int MPEG::Properties::bitrate() const
96 {
97   return d->bitrate;
98 }
99
100 int MPEG::Properties::sampleRate() const
101 {
102   return d->sampleRate;
103 }
104
105 int MPEG::Properties::channels() const
106 {
107   return d->channels;
108 }
109
110 const MPEG::XingHeader *MPEG::Properties::xingHeader() const
111 {
112   return d->xingHeader;
113 }
114
115 MPEG::Header::Version MPEG::Properties::version() const
116 {
117   return d->version;
118 }
119
120 int MPEG::Properties::layer() const
121 {
122   return d->layer;
123 }
124
125 bool MPEG::Properties::protectionEnabled() const
126 {
127   return d->protectionEnabled;
128 }
129
130 MPEG::Header::ChannelMode MPEG::Properties::channelMode() const
131 {
132   return d->channelMode;
133 }
134
135 bool MPEG::Properties::isCopyrighted() const
136 {
137   return d->isCopyrighted;
138 }
139
140 bool MPEG::Properties::isOriginal() const
141 {
142   return d->isOriginal;
143 }
144
145 ////////////////////////////////////////////////////////////////////////////////
146 // private members
147 ////////////////////////////////////////////////////////////////////////////////
148
149 void MPEG::Properties::read()
150 {
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.
153
154   long last = d->file->lastFrameOffset();
155
156   if(last < 0) {
157     debug("MPEG::Properties::read() -- Could not find a valid last MPEG frame in the stream.");
158     return;
159   }
160
161   d->file->seek(last);
162   Header lastHeader(d->file->readBlock(4));
163
164   long first = d->file->firstFrameOffset();
165
166   if(first < 0) {
167     debug("MPEG::Properties::read() -- Could not find a valid first MPEG frame in the stream.");
168     return;
169   }
170
171   if(!lastHeader.isValid()) {
172
173     long pos = last;
174
175     while(pos > first) {
176
177       pos = d->file->previousFrameOffset(pos);
178
179       if(pos < 0)
180         break;
181
182       d->file->seek(pos);
183       Header header(d->file->readBlock(4));
184
185       if(header.isValid()) {
186         lastHeader = header;
187         last = pos;
188         break;
189       }
190     }
191   }
192
193   // Now jump back to the front of the file and read what we need from there.
194
195   d->file->seek(first);
196   Header firstHeader(d->file->readBlock(4));
197
198   if(!firstHeader.isValid() || !lastHeader.isValid()) {
199     debug("MPEG::Properties::read() -- Page headers were invalid.");
200     return;
201   }
202
203   // Check for a Xing header that will help us in gathering information about a
204   // VBR stream.
205
206   int xingHeaderOffset = MPEG::XingHeader::xingHeaderOffset(firstHeader.version(),
207                                                             firstHeader.channelMode());
208
209   d->file->seek(first + xingHeaderOffset);
210   d->xingHeader = new XingHeader(d->file->readBlock(16));
211
212   // Read the length and the bitrate from the Xing header.
213
214   if(d->xingHeader->isValid() &&
215      firstHeader.sampleRate() > 0 &&
216      d->xingHeader->totalFrames() > 0)
217   {
218       double timePerFrame =
219         double(firstHeader.samplesPerFrame()) / firstHeader.sampleRate();
220
221       double length = timePerFrame * d->xingHeader->totalFrames();
222
223       d->length = int(length);
224       d->bitrate = d->length > 0 ? d->xingHeader->totalSize() * 8 / length / 1000 : 0;
225   }
226   else {
227     // Since there was no valid Xing header found, we hope that we're in a constant
228     // bitrate file.
229
230     delete d->xingHeader;
231     d->xingHeader = 0;
232
233     // TODO: Make this more robust with audio property detection for VBR without a
234     // Xing header.
235
236     if(firstHeader.frameLength() > 0 && firstHeader.bitrate() > 0) {
237       int frames = (last - first) / firstHeader.frameLength() + 1;
238
239       d->length = int(float(firstHeader.frameLength() * frames) /
240                       float(firstHeader.bitrate() * 125) + 0.5);
241       d->bitrate = firstHeader.bitrate();
242     }
243   }
244
245
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();
254 }