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 <climits>
27 
28 #include <tdebug.h>
29 #include <tstring.h>
30 #include "mp4atom.h"
31 
32 using namespace TagLib;
33 
34 const char *MP4::Atom::containers[11] = {
35     "moov", "udta", "mdia", "meta", "ilst",
36     "stbl", "minf", "moof", "traf", "trak",
37     "stsd"
38 };
39 
Atom(File * file)40 MP4::Atom::Atom(File *file)
41 {
42   children.setAutoDelete(true);
43 
44   offset = file->tell();
45   ByteVector header = file->readBlock(8);
46   if(header.size() != 8) {
47     // The atom header must be 8 bytes long, otherwise there is either
48     // trailing garbage or the file is truncated
49     debug("MP4: Couldn't read 8 bytes of data for atom header");
50     length = 0;
51     file->seek(0, File::End);
52     return;
53   }
54 
55   length = header.toUInt();
56 
57   if(length == 0) {
58     // The last atom which extends to the end of the file.
59     length = file->length() - offset;
60   }
61   else if(length == 1) {
62     // The atom has a 64-bit length.
63     const long long longLength = file->readBlock(8).toLongLong();
64     if(longLength <= LONG_MAX) {
65       // The actual length fits in long. That's always the case if long is 64-bit.
66       length = static_cast<long>(longLength);
67     }
68     else {
69       debug("MP4: 64-bit atoms are not supported");
70       length = 0;
71       file->seek(0, File::End);
72       return;
73     }
74   }
75 
76   if(length < 8) {
77     debug("MP4: Invalid atom size");
78     length = 0;
79     file->seek(0, File::End);
80     return;
81   }
82 
83   name = header.mid(4, 4);
84 
85   for(int i = 0; i < numContainers; i++) {
86     if(name == containers[i]) {
87       if(name == "meta") {
88         file->seek(4, File::Current);
89       }
90       else if(name == "stsd") {
91         file->seek(8, File::Current);
92       }
93       while(file->tell() < offset + length) {
94         MP4::Atom *child = new MP4::Atom(file);
95         children.append(child);
96         if(child->length == 0)
97           return;
98       }
99       return;
100     }
101   }
102 
103   file->seek(offset + length);
104 }
105 
~Atom()106 MP4::Atom::~Atom()
107 {
108 }
109 
110 MP4::Atom *
find(const char * name1,const char * name2,const char * name3,const char * name4)111 MP4::Atom::find(const char *name1, const char *name2, const char *name3, const char *name4)
112 {
113   if(name1 == 0) {
114     return this;
115   }
116   for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
117     if((*it)->name == name1) {
118       return (*it)->find(name2, name3, name4);
119     }
120   }
121   return 0;
122 }
123 
124 MP4::AtomList
findall(const char * name,bool recursive)125 MP4::Atom::findall(const char *name, bool recursive)
126 {
127   MP4::AtomList result;
128   for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
129     if((*it)->name == name) {
130       result.append(*it);
131     }
132     if(recursive) {
133       result.append((*it)->findall(name, recursive));
134     }
135   }
136   return result;
137 }
138 
139 bool
path(MP4::AtomList & path,const char * name1,const char * name2,const char * name3)140 MP4::Atom::path(MP4::AtomList &path, const char *name1, const char *name2, const char *name3)
141 {
142   path.append(this);
143   if(name1 == 0) {
144     return true;
145   }
146   for(AtomList::ConstIterator it = children.begin(); it != children.end(); ++it) {
147     if((*it)->name == name1) {
148       return (*it)->path(path, name2, name3);
149     }
150   }
151   return false;
152 }
153 
Atoms(File * file)154 MP4::Atoms::Atoms(File *file)
155 {
156   atoms.setAutoDelete(true);
157 
158   file->seek(0, File::End);
159   long end = file->tell();
160   file->seek(0);
161   while(file->tell() + 8 <= end) {
162     MP4::Atom *atom = new MP4::Atom(file);
163     atoms.append(atom);
164     if (atom->length == 0)
165       break;
166   }
167 }
168 
~Atoms()169 MP4::Atoms::~Atoms()
170 {
171 }
172 
173 MP4::Atom *
find(const char * name1,const char * name2,const char * name3,const char * name4)174 MP4::Atoms::find(const char *name1, const char *name2, const char *name3, const char *name4)
175 {
176   for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
177     if((*it)->name == name1) {
178       return (*it)->find(name2, name3, name4);
179     }
180   }
181   return 0;
182 }
183 
184 MP4::AtomList
path(const char * name1,const char * name2,const char * name3,const char * name4)185 MP4::Atoms::path(const char *name1, const char *name2, const char *name3, const char *name4)
186 {
187   MP4::AtomList path;
188   for(AtomList::ConstIterator it = atoms.begin(); it != atoms.end(); ++it) {
189     if((*it)->name == name1) {
190       if(!(*it)->path(path, name2, name3, name4)) {
191         path.clear();
192       }
193       return path;
194     }
195   }
196   return path;
197 }
198