1 /**************************************************************************
2 copyright : (C) 2007 by Lukáš Lalinský
3 email : lalinsky@gmail.com
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 #include "mp4file.h"
29 #include "mp4atom.h"
30 #include "mp4properties.h"
31
32 using namespace TagLib;
33
34 class MP4::Properties::PropertiesPrivate
35 {
36 public:
PropertiesPrivate()37 PropertiesPrivate() :
38 length(0),
39 bitrate(0),
40 sampleRate(0),
41 channels(0),
42 bitsPerSample(0),
43 encrypted(false),
44 codec(MP4::Properties::Unknown) {}
45
46 int length;
47 int bitrate;
48 int sampleRate;
49 int channels;
50 int bitsPerSample;
51 bool encrypted;
52 Codec codec;
53 };
54
55 ////////////////////////////////////////////////////////////////////////////////
56 // public members
57 ////////////////////////////////////////////////////////////////////////////////
58
Properties(File * file,MP4::Atoms * atoms,ReadStyle style)59 MP4::Properties::Properties(File *file, MP4::Atoms *atoms, ReadStyle style) :
60 AudioProperties(style),
61 d(new PropertiesPrivate())
62 {
63 read(file, atoms);
64 }
65
~Properties()66 MP4::Properties::~Properties()
67 {
68 delete d;
69 }
70
71 int
channels() const72 MP4::Properties::channels() const
73 {
74 return d->channels;
75 }
76
77 int
sampleRate() const78 MP4::Properties::sampleRate() const
79 {
80 return d->sampleRate;
81 }
82
83 int
length() const84 MP4::Properties::length() const
85 {
86 return lengthInSeconds();
87 }
88
89 int
lengthInSeconds() const90 MP4::Properties::lengthInSeconds() const
91 {
92 return d->length / 1000;
93 }
94
95 int
lengthInMilliseconds() const96 MP4::Properties::lengthInMilliseconds() const
97 {
98 return d->length;
99 }
100
101 int
bitrate() const102 MP4::Properties::bitrate() const
103 {
104 return d->bitrate;
105 }
106
107 int
bitsPerSample() const108 MP4::Properties::bitsPerSample() const
109 {
110 return d->bitsPerSample;
111 }
112
113 bool
isEncrypted() const114 MP4::Properties::isEncrypted() const
115 {
116 return d->encrypted;
117 }
118
119 MP4::Properties::Codec
codec() const120 MP4::Properties::codec() const
121 {
122 return d->codec;
123 }
124
125 ////////////////////////////////////////////////////////////////////////////////
126 // private members
127 ////////////////////////////////////////////////////////////////////////////////
128
129 void
read(File * file,Atoms * atoms)130 MP4::Properties::read(File *file, Atoms *atoms)
131 {
132 MP4::Atom *moov = atoms->find("moov");
133 if(!moov) {
134 debug("MP4: Atom 'moov' not found");
135 return;
136 }
137
138 MP4::Atom *trak = 0;
139 ByteVector data;
140
141 const MP4::AtomList trakList = moov->findall("trak");
142 for(MP4::AtomList::ConstIterator it = trakList.begin(); it != trakList.end(); ++it) {
143 trak = *it;
144 MP4::Atom *hdlr = trak->find("mdia", "hdlr");
145 if(!hdlr) {
146 debug("MP4: Atom 'trak.mdia.hdlr' not found");
147 return;
148 }
149 file->seek(hdlr->offset);
150 data = file->readBlock(hdlr->length);
151 if(data.containsAt("soun", 16)) {
152 break;
153 }
154 trak = 0;
155 }
156 if(!trak) {
157 debug("MP4: No audio tracks");
158 return;
159 }
160
161 MP4::Atom *mdhd = trak->find("mdia", "mdhd");
162 if(!mdhd) {
163 debug("MP4: Atom 'trak.mdia.mdhd' not found");
164 return;
165 }
166
167 file->seek(mdhd->offset);
168 data = file->readBlock(mdhd->length);
169
170 const unsigned int version = data[8];
171 long long unit;
172 long long length;
173 if(version == 1) {
174 if(data.size() < 36 + 8) {
175 debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
176 return;
177 }
178 unit = data.toUInt(28U);
179 length = data.toLongLong(32U);
180 }
181 else {
182 if(data.size() < 24 + 8) {
183 debug("MP4: Atom 'trak.mdia.mdhd' is smaller than expected");
184 return;
185 }
186 unit = data.toUInt(20U);
187 length = data.toUInt(24U);
188 }
189 if(unit > 0 && length > 0)
190 d->length = static_cast<int>(length * 1000.0 / unit + 0.5);
191
192 MP4::Atom *atom = trak->find("mdia", "minf", "stbl", "stsd");
193 if(!atom) {
194 return;
195 }
196
197 file->seek(atom->offset);
198 data = file->readBlock(atom->length);
199 if(data.containsAt("mp4a", 20)) {
200 d->codec = AAC;
201 d->channels = data.toShort(40U);
202 d->bitsPerSample = data.toShort(42U);
203 d->sampleRate = data.toUInt(46U);
204 if(data.containsAt("esds", 56) && data[64] == 0x03) {
205 unsigned int pos = 65;
206 if(data.containsAt("\x80\x80\x80", pos)) {
207 pos += 3;
208 }
209 pos += 4;
210 if(data[pos] == 0x04) {
211 pos += 1;
212 if(data.containsAt("\x80\x80\x80", pos)) {
213 pos += 3;
214 }
215 pos += 10;
216 d->bitrate = static_cast<int>((data.toUInt(pos) + 500) / 1000.0 + 0.5);
217 }
218 }
219 }
220 else if(data.containsAt("alac", 20)) {
221 if(atom->length == 88 && data.containsAt("alac", 56)) {
222 d->codec = ALAC;
223 d->bitsPerSample = data.at(69);
224 d->channels = data.at(73);
225 d->bitrate = static_cast<int>(data.toUInt(80U) / 1000.0 + 0.5);
226 d->sampleRate = data.toUInt(84U);
227 }
228 }
229
230 MP4::Atom *drms = atom->find("drms");
231 if(drms) {
232 d->encrypted = true;
233 }
234 }
235