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