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