[SPIP][PLUGINS] v3.0-->v3.2
[lhc/web/www.git] / www / plugins-dist / medias / lib / getid3 / module.audio-video.quicktime.php
1 <?php
2 /////////////////////////////////////////////////////////////////
3 /// getID3() by James Heinrich <info@getid3.org> //
4 // available at http://getid3.sourceforge.net //
5 // or http://www.getid3.org //
6 // also https://github.com/JamesHeinrich/getID3 //
7 /////////////////////////////////////////////////////////////////
8 // See readme.txt for more details //
9 /////////////////////////////////////////////////////////////////
10 // //
11 // module.audio-video.quicktime.php //
12 // module for analyzing Quicktime and MP3-in-MP4 files //
13 // dependencies: module.audio.mp3.php //
14 // dependencies: module.tag.id3v2.php //
15 // ///
16 /////////////////////////////////////////////////////////////////
17
18 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.audio.mp3.php', __FILE__, true);
19 getid3_lib::IncludeDependency(GETID3_INCLUDEPATH.'module.tag.id3v2.php', __FILE__, true); // needed for ISO 639-2 language code lookup
20
21 class getid3_quicktime extends getid3_handler
22 {
23
24 public $ReturnAtomData = true;
25 public $ParseAllPossibleAtoms = false;
26
27 public function Analyze() {
28 $info = &$this->getid3->info;
29
30 $info['fileformat'] = 'quicktime';
31 $info['quicktime']['hinting'] = false;
32 $info['quicktime']['controller'] = 'standard'; // may be overridden if 'ctyp' atom is present
33
34 $this->fseek($info['avdataoffset']);
35
36 $offset = 0;
37 $atomcounter = 0;
38 $atom_data_read_buffer_size = max($this->getid3->option_fread_buffer_size * 1024, ($info['php_memory_limit'] ? round($info['php_memory_limit'] / 4) : 1024)); // set read buffer to 25% of PHP memory limit (if one is specified), otherwise use option_fread_buffer_size [default: 32MB]
39 while ($offset < $info['avdataend']) {
40 if (!getid3_lib::intValueSupported($offset)) {
41 $this->error('Unable to parse atom at offset '.$offset.' because beyond '.round(PHP_INT_MAX / 1073741824).'GB limit of PHP filesystem functions');
42 break;
43 }
44 $this->fseek($offset);
45 $AtomHeader = $this->fread(8);
46
47 $atomsize = getid3_lib::BigEndian2Int(substr($AtomHeader, 0, 4));
48 $atomname = substr($AtomHeader, 4, 4);
49
50 // 64-bit MOV patch by jlegateØktnc*com
51 if ($atomsize == 1) {
52 $atomsize = getid3_lib::BigEndian2Int($this->fread(8));
53 }
54
55 $info['quicktime'][$atomname]['name'] = $atomname;
56 $info['quicktime'][$atomname]['size'] = $atomsize;
57 $info['quicktime'][$atomname]['offset'] = $offset;
58
59 if (($offset + $atomsize) > $info['avdataend']) {
60 $this->error('Atom at offset '.$offset.' claims to go beyond end-of-file (length: '.$atomsize.' bytes)');
61 return false;
62 }
63
64 if ($atomsize == 0) {
65 // Furthermore, for historical reasons the list of atoms is optionally
66 // terminated by a 32-bit integer set to 0. If you are writing a program
67 // to read user data atoms, you should allow for the terminating 0.
68 break;
69 }
70 $atomHierarchy = array();
71 $info['quicktime'][$atomname] = $this->QuicktimeParseAtom($atomname, $atomsize, $this->fread(min($atomsize, $atom_data_read_buffer_size)), $offset, $atomHierarchy, $this->ParseAllPossibleAtoms);
72
73 $offset += $atomsize;
74 $atomcounter++;
75 }
76
77 if (!empty($info['avdataend_tmp'])) {
78 // this value is assigned to a temp value and then erased because
79 // otherwise any atoms beyond the 'mdat' atom would not get parsed
80 $info['avdataend'] = $info['avdataend_tmp'];
81 unset($info['avdataend_tmp']);
82 }
83
84 if (!empty($info['quicktime']['comments']['chapters']) && is_array($info['quicktime']['comments']['chapters']) && (count($info['quicktime']['comments']['chapters']) > 0)) {
85 $durations = $this->quicktime_time_to_sample_table($info);
86 for ($i = 0; $i < count($info['quicktime']['comments']['chapters']); $i++) {
87 $bookmark = array();
88 $bookmark['title'] = $info['quicktime']['comments']['chapters'][$i];
89 if (isset($durations[$i])) {
90 $bookmark['duration_sample'] = $durations[$i]['sample_duration'];
91 if ($i > 0) {
92 $bookmark['start_sample'] = $info['quicktime']['bookmarks'][($i - 1)]['start_sample'] + $info['quicktime']['bookmarks'][($i - 1)]['duration_sample'];
93 } else {
94 $bookmark['start_sample'] = 0;
95 }
96 if ($time_scale = $this->quicktime_bookmark_time_scale($info)) {
97 $bookmark['duration_seconds'] = $bookmark['duration_sample'] / $time_scale;
98 $bookmark['start_seconds'] = $bookmark['start_sample'] / $time_scale;
99 }
100 }
101 $info['quicktime']['bookmarks'][] = $bookmark;
102 }
103 }
104
105 if (isset($info['quicktime']['temp_meta_key_names'])) {
106 unset($info['quicktime']['temp_meta_key_names']);
107 }
108
109 if (!empty($info['quicktime']['comments']['location.ISO6709'])) {
110 // https://en.wikipedia.org/wiki/ISO_6709
111 foreach ($info['quicktime']['comments']['location.ISO6709'] as $ISO6709string) {
112 $latitude = false;
113 $longitude = false;
114 $altitude = false;
115 if (preg_match('#^([\\+\\-])([0-9]{2}|[0-9]{4}|[0-9]{6})(\\.[0-9]+)?([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?(([\\+\\-])([0-9]{3}|[0-9]{5}|[0-9]{7})(\\.[0-9]+)?)?/$#', $ISO6709string, $matches)) {
116 @list($dummy, $lat_sign, $lat_deg, $lat_deg_dec, $lon_sign, $lon_deg, $lon_deg_dec, $dummy, $alt_sign, $alt_deg, $alt_deg_dec) = $matches;
117
118 if (strlen($lat_deg) == 2) { // [+-]DD.D
119 $latitude = floatval(ltrim($lat_deg, '0').$lat_deg_dec);
120 } elseif (strlen($lat_deg) == 4) { // [+-]DDMM.M
121 $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0').$lat_deg_dec / 60);
122 } elseif (strlen($lat_deg) == 6) { // [+-]DDMMSS.S
123 $latitude = floatval(ltrim(substr($lat_deg, 0, 2), '0')) + floatval(ltrim(substr($lat_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lat_deg, 4, 2), '0').$lat_deg_dec / 3600);
124 }
125
126 if (strlen($lon_deg) == 3) { // [+-]DDD.D
127 $longitude = floatval(ltrim($lon_deg, '0').$lon_deg_dec);
128 } elseif (strlen($lon_deg) == 5) { // [+-]DDDMM.M
129 $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0').$lon_deg_dec / 60);
130 } elseif (strlen($lon_deg) == 7) { // [+-]DDDMMSS.S
131 $longitude = floatval(ltrim(substr($lon_deg, 0, 2), '0')) + floatval(ltrim(substr($lon_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($lon_deg, 4, 2), '0').$lon_deg_dec / 3600);
132 }
133
134 if (strlen($alt_deg) == 3) { // [+-]DDD.D
135 $altitude = floatval(ltrim($alt_deg, '0').$alt_deg_dec);
136 } elseif (strlen($alt_deg) == 5) { // [+-]DDDMM.M
137 $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0').$alt_deg_dec / 60);
138 } elseif (strlen($alt_deg) == 7) { // [+-]DDDMMSS.S
139 $altitude = floatval(ltrim(substr($alt_deg, 0, 2), '0')) + floatval(ltrim(substr($alt_deg, 2, 2), '0') / 60) + floatval(ltrim(substr($alt_deg, 4, 2), '0').$alt_deg_dec / 3600);
140 }
141
142 if ($latitude !== false) {
143 $info['quicktime']['comments']['gps_latitude'][] = (($lat_sign == '-') ? -1 : 1) * floatval($latitude);
144 }
145 if ($longitude !== false) {
146 $info['quicktime']['comments']['gps_longitude'][] = (($lon_sign == '-') ? -1 : 1) * floatval($longitude);
147 }
148 if ($altitude !== false) {
149 $info['quicktime']['comments']['gps_altitude'][] = (($alt_sign == '-') ? -1 : 1) * floatval($altitude);
150 }
151 }
152 if ($latitude === false) {
153 $this->warning('location.ISO6709 string not parsed correctly: "'.$ISO6709string.'", please submit as a bug');
154 }
155 break;
156 }
157 }
158
159 if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) {
160 $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
161 }
162 if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info['quicktime']['video'])) {
163 $info['audio']['bitrate'] = $info['bitrate'];
164 }
165 if (!empty($info['playtime_seconds']) && !isset($info['video']['frame_rate']) && !empty($info['quicktime']['stts_framecount'])) {
166 foreach ($info['quicktime']['stts_framecount'] as $key => $samples_count) {
167 $samples_per_second = $samples_count / $info['playtime_seconds'];
168 if ($samples_per_second > 240) {
169 // has to be audio samples
170 } else {
171 $info['video']['frame_rate'] = $samples_per_second;
172 break;
173 }
174 }
175 }
176 if ($info['audio']['dataformat'] == 'mp4') {
177 $info['fileformat'] = 'mp4';
178 if (empty($info['video']['resolution_x'])) {
179 $info['mime_type'] = 'audio/mp4';
180 unset($info['video']['dataformat']);
181 } else {
182 $info['mime_type'] = 'video/mp4';
183 }
184 }
185
186 if (!$this->ReturnAtomData) {
187 unset($info['quicktime']['moov']);
188 }
189
190 if (empty($info['audio']['dataformat']) && !empty($info['quicktime']['audio'])) {
191 $info['audio']['dataformat'] = 'quicktime';
192 }
193 if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) {
194 $info['video']['dataformat'] = 'quicktime';
195 }
196
197 return true;
198 }
199
200 public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
201 // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm
202 // https://code.google.com/p/mp4v2/wiki/iTunesMetadata
203
204 $info = &$this->getid3->info;
205
206 $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717
207 array_push($atomHierarchy, $atomname);
208 $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
209 $atom_structure['name'] = $atomname;
210 $atom_structure['size'] = $atomsize;
211 $atom_structure['offset'] = $baseoffset;
212 switch ($atomname) {
213 case 'moov': // MOVie container atom
214 case 'trak': // TRAcK container atom
215 case 'clip': // CLIPping container atom
216 case 'matt': // track MATTe container atom
217 case 'edts': // EDiTS container atom
218 case 'tref': // Track REFerence container atom
219 case 'mdia': // MeDIA container atom
220 case 'minf': // Media INFormation container atom
221 case 'dinf': // Data INFormation container atom
222 case 'udta': // User DaTA container atom
223 case 'cmov': // Compressed MOVie container atom
224 case 'rmra': // Reference Movie Record Atom
225 case 'rmda': // Reference Movie Descriptor Atom
226 case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR)
227 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
228 break;
229
230 case 'ilst': // Item LiST container atom
231 if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) {
232 // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted
233 $allnumericnames = true;
234 foreach ($atom_structure['subatoms'] as $subatomarray) {
235 if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
236 $allnumericnames = false;
237 break;
238 }
239 }
240 if ($allnumericnames) {
241 $newData = array();
242 foreach ($atom_structure['subatoms'] as $subatomarray) {
243 foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
244 unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
245 $newData[$subatomarray['name']] = $newData_subatomarray;
246 break;
247 }
248 }
249 $atom_structure['data'] = $newData;
250 unset($atom_structure['subatoms']);
251 }
252 }
253 break;
254
255 case "\x00\x00\x00\x01":
256 case "\x00\x00\x00\x02":
257 case "\x00\x00\x00\x03":
258 case "\x00\x00\x00\x04":
259 case "\x00\x00\x00\x05":
260 $atomname = getid3_lib::BigEndian2Int($atomname);
261 $atom_structure['name'] = $atomname;
262 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
263 break;
264
265 case 'stbl': // Sample TaBLe container atom
266 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
267 $isVideo = false;
268 $framerate = 0;
269 $framecount = 0;
270 foreach ($atom_structure['subatoms'] as $key => $value_array) {
271 if (isset($value_array['sample_description_table'])) {
272 foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
273 if (isset($value_array2['data_format'])) {
274 switch ($value_array2['data_format']) {
275 case 'avc1':
276 case 'mp4v':
277 // video data
278 $isVideo = true;
279 break;
280 case 'mp4a':
281 // audio data
282 break;
283 }
284 }
285 }
286 } elseif (isset($value_array['time_to_sample_table'])) {
287 foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
288 if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
289 $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3);
290 $framecount = $value_array2['sample_count'];
291 }
292 }
293 }
294 }
295 if ($isVideo && $framerate) {
296 $info['quicktime']['video']['frame_rate'] = $framerate;
297 $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate'];
298 }
299 if ($isVideo && $framecount) {
300 $info['quicktime']['video']['frame_count'] = $framecount;
301 }
302 break;
303
304
305 case "\xA9".'alb': // ALBum
306 case "\xA9".'ART': //
307 case "\xA9".'art': // ARTist
308 case "\xA9".'aut': //
309 case "\xA9".'cmt': // CoMmenT
310 case "\xA9".'com': // COMposer
311 case "\xA9".'cpy': //
312 case "\xA9".'day': // content created year
313 case "\xA9".'dir': //
314 case "\xA9".'ed1': //
315 case "\xA9".'ed2': //
316 case "\xA9".'ed3': //
317 case "\xA9".'ed4': //
318 case "\xA9".'ed5': //
319 case "\xA9".'ed6': //
320 case "\xA9".'ed7': //
321 case "\xA9".'ed8': //
322 case "\xA9".'ed9': //
323 case "\xA9".'enc': //
324 case "\xA9".'fmt': //
325 case "\xA9".'gen': // GENre
326 case "\xA9".'grp': // GRouPing
327 case "\xA9".'hst': //
328 case "\xA9".'inf': //
329 case "\xA9".'lyr': // LYRics
330 case "\xA9".'mak': //
331 case "\xA9".'mod': //
332 case "\xA9".'nam': // full NAMe
333 case "\xA9".'ope': //
334 case "\xA9".'PRD': //
335 case "\xA9".'prf': //
336 case "\xA9".'req': //
337 case "\xA9".'src': //
338 case "\xA9".'swr': //
339 case "\xA9".'too': // encoder
340 case "\xA9".'trk': // TRacK
341 case "\xA9".'url': //
342 case "\xA9".'wrn': //
343 case "\xA9".'wrt': // WRiTer
344 case '----': // itunes specific
345 case 'aART': // Album ARTist
346 case 'akID': // iTunes store account type
347 case 'apID': // Purchase Account
348 case 'atID': //
349 case 'catg': // CaTeGory
350 case 'cmID': //
351 case 'cnID': //
352 case 'covr': // COVeR artwork
353 case 'cpil': // ComPILation
354 case 'cprt': // CoPyRighT
355 case 'desc': // DESCription
356 case 'disk': // DISK number
357 case 'egid': // Episode Global ID
358 case 'geID': //
359 case 'gnre': // GeNRE
360 case 'hdvd': // HD ViDeo
361 case 'keyw': // KEYWord
362 case 'ldes': // Long DEScription
363 case 'pcst': // PodCaST
364 case 'pgap': // GAPless Playback
365 case 'plID': //
366 case 'purd': // PURchase Date
367 case 'purl': // Podcast URL
368 case 'rati': //
369 case 'rndu': //
370 case 'rpdu': //
371 case 'rtng': // RaTiNG
372 case 'sfID': // iTunes store country
373 case 'soaa': // SOrt Album Artist
374 case 'soal': // SOrt ALbum
375 case 'soar': // SOrt ARtist
376 case 'soco': // SOrt COmposer
377 case 'sonm': // SOrt NaMe
378 case 'sosn': // SOrt Show Name
379 case 'stik': //
380 case 'tmpo': // TeMPO (BPM)
381 case 'trkn': // TRacK Number
382 case 'tven': // tvEpisodeID
383 case 'tves': // TV EpiSode
384 case 'tvnn': // TV Network Name
385 case 'tvsh': // TV SHow Name
386 case 'tvsn': // TV SeasoN
387 if ($atom_parent == 'udta') {
388 // User data atom handler
389 $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
390 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
391 $atom_structure['data'] = substr($atom_data, 4);
392
393 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
394 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
395 $info['comments']['language'][] = $atom_structure['language'];
396 }
397 } else {
398 // Apple item list box atom handler
399 $atomoffset = 0;
400 if (substr($atom_data, 2, 2) == "\x10\xB5") {
401 // not sure what it means, but observed on iPhone4 data.
402 // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data
403 while ($atomoffset < strlen($atom_data)) {
404 $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2));
405 $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
406 $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
407 if ($boxsmallsize <= 1) {
408 $this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
409 $atom_structure['data'] = null;
410 $atomoffset = strlen($atom_data);
411 break;
412 }
413 switch ($boxsmalltype) {
414 case "\x10\xB5":
415 $atom_structure['data'] = $boxsmalldata;
416 break;
417 default:
418 $this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset);
419 $atom_structure['data'] = $atom_data;
420 break;
421 }
422 $atomoffset += (4 + $boxsmallsize);
423 }
424 } else {
425 while ($atomoffset < strlen($atom_data)) {
426 $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4));
427 $boxtype = substr($atom_data, $atomoffset + 4, 4);
428 $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
429 if ($boxsize <= 1) {
430 $this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset));
431 $atom_structure['data'] = null;
432 $atomoffset = strlen($atom_data);
433 break;
434 }
435 $atomoffset += $boxsize;
436
437 switch ($boxtype) {
438 case 'mean':
439 case 'name':
440 $atom_structure[$boxtype] = substr($boxdata, 4);
441 break;
442
443 case 'data':
444 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1));
445 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3));
446 switch ($atom_structure['flags_raw']) {
447 case 0: // data flag
448 case 21: // tmpo/cpil flag
449 switch ($atomname) {
450 case 'cpil':
451 case 'hdvd':
452 case 'pcst':
453 case 'pgap':
454 // 8-bit integer (boolean)
455 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
456 break;
457
458 case 'tmpo':
459 // 16-bit integer
460 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2));
461 break;
462
463 case 'disk':
464 case 'trkn':
465 // binary
466 $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2));
467 $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2));
468 $atom_structure['data'] = empty($num) ? '' : $num;
469 $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
470 break;
471
472 case 'gnre':
473 // enum
474 $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
475 $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1);
476 break;
477
478 case 'rtng':
479 // 8-bit integer
480 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
481 $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]);
482 break;
483
484 case 'stik':
485 // 8-bit integer (enum)
486 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1));
487 $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]);
488 break;
489
490 case 'sfID':
491 // 32-bit integer
492 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
493 $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]);
494 break;
495
496 case 'egid':
497 case 'purl':
498 $atom_structure['data'] = substr($boxdata, 8);
499 break;
500
501 case 'plID':
502 // 64-bit integer
503 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8));
504 break;
505
506 case 'covr':
507 $atom_structure['data'] = substr($boxdata, 8);
508 // not a foolproof check, but better than nothing
509 if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
510 $atom_structure['image_mime'] = 'image/jpeg';
511 } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
512 $atom_structure['image_mime'] = 'image/png';
513 } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
514 $atom_structure['image_mime'] = 'image/gif';
515 }
516 break;
517
518 case 'atID':
519 case 'cnID':
520 case 'geID':
521 case 'tves':
522 case 'tvsn':
523 default:
524 // 32-bit integer
525 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4));
526 }
527 break;
528
529 case 1: // text flag
530 case 13: // image flag
531 default:
532 $atom_structure['data'] = substr($boxdata, 8);
533 if ($atomname == 'covr') {
534 // not a foolproof check, but better than nothing
535 if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) {
536 $atom_structure['image_mime'] = 'image/jpeg';
537 } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) {
538 $atom_structure['image_mime'] = 'image/png';
539 } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
540 $atom_structure['image_mime'] = 'image/gif';
541 }
542 }
543 break;
544
545 }
546 break;
547
548 default:
549 $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset);
550 $atom_structure['data'] = $atom_data;
551
552 }
553 }
554 }
555 }
556 $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']);
557 break;
558
559
560 case 'play': // auto-PLAY atom
561 $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
562
563 $info['quicktime']['autoplay'] = $atom_structure['autoplay'];
564 break;
565
566
567 case 'WLOC': // Window LOCation atom
568 $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
569 $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2));
570 break;
571
572
573 case 'LOOP': // LOOPing atom
574 case 'SelO': // play SELection Only atom
575 case 'AllF': // play ALL Frames atom
576 $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data);
577 break;
578
579
580 case 'name': //
581 case 'MCPS': // Media Cleaner PRo
582 case '@PRM': // adobe PReMiere version
583 case '@PRQ': // adobe PRemiere Quicktime version
584 $atom_structure['data'] = $atom_data;
585 break;
586
587
588 case 'cmvd': // Compressed MooV Data atom
589 // Code by ubergeekØubergeek*tv based on information from
590 // http://developer.apple.com/quicktime/icefloe/dispatch012.html
591 $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
592
593 $CompressedFileData = substr($atom_data, 4);
594 if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
595 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms);
596 } else {
597 $this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']);
598 }
599 break;
600
601
602 case 'dcom': // Data COMpression atom
603 $atom_structure['compression_id'] = $atom_data;
604 $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data);
605 break;
606
607
608 case 'rdrf': // Reference movie Data ReFerence atom
609 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
610 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
611 $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
612
613 $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
614 $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
615 switch ($atom_structure['reference_type_name']) {
616 case 'url ':
617 $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12));
618 break;
619
620 case 'alis':
621 $atom_structure['file_alias'] = substr($atom_data, 12);
622 break;
623
624 case 'rsrc':
625 $atom_structure['resource_alias'] = substr($atom_data, 12);
626 break;
627
628 default:
629 $atom_structure['data'] = substr($atom_data, 12);
630 break;
631 }
632 break;
633
634
635 case 'rmqu': // Reference Movie QUality atom
636 $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data);
637 break;
638
639
640 case 'rmcs': // Reference Movie Cpu Speed atom
641 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
642 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
643 $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
644 break;
645
646
647 case 'rmvc': // Reference Movie Version Check atom
648 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
649 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
650 $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
651 $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
652 $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
653 $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
654 break;
655
656
657 case 'rmcd': // Reference Movie Component check atom
658 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
659 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
660 $atom_structure['component_type'] = substr($atom_data, 4, 4);
661 $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
662 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
663 $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
664 $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
665 $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4));
666 break;
667
668
669 case 'rmdr': // Reference Movie Data Rate atom
670 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
671 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
672 $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
673
674 $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
675 break;
676
677
678 case 'rmla': // Reference Movie Language Atom
679 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
680 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
681 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
682
683 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
684 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
685 $info['comments']['language'][] = $atom_structure['language'];
686 }
687 break;
688
689
690 case 'rmla': // Reference Movie Language Atom
691 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
692 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
693 $atom_structure['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
694 break;
695
696
697 case 'ptv ': // Print To Video - defines a movie's full screen mode
698 // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm
699 $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2));
700 $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000
701 $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000
702 $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1));
703 $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1));
704
705 $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
706 $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
707
708 $ptv_lookup[0] = 'normal';
709 $ptv_lookup[1] = 'double';
710 $ptv_lookup[2] = 'half';
711 $ptv_lookup[3] = 'full';
712 $ptv_lookup[4] = 'current';
713 if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
714 $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
715 } else {
716 $this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')');
717 }
718 break;
719
720
721 case 'stsd': // Sample Table Sample Description atom
722 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
723 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
724 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
725 $stsdEntriesDataOffset = 8;
726 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
727 $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
728 $stsdEntriesDataOffset += 4;
729 $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
730 $stsdEntriesDataOffset += 4;
731 $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
732 $stsdEntriesDataOffset += 6;
733 $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
734 $stsdEntriesDataOffset += 2;
735 $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
736 $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
737
738 $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2));
739 $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2));
740 $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
741
742 switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
743
744 case "\x00\x00\x00\x00":
745 // audio tracks
746 $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2));
747 $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2));
748 $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2));
749 $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2));
750 $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4));
751
752 // video tracks
753 // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html
754 $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
755 $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
756 $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
757 $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
758 $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
759 $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
760 $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4));
761 $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2));
762 $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4);
763 $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2));
764 $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2));
765
766 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
767 case '2vuY':
768 case 'avc1':
769 case 'cvid':
770 case 'dvc ':
771 case 'dvcp':
772 case 'gif ':
773 case 'h263':
774 case 'jpeg':
775 case 'kpcd':
776 case 'mjpa':
777 case 'mjpb':
778 case 'mp4v':
779 case 'png ':
780 case 'raw ':
781 case 'rle ':
782 case 'rpza':
783 case 'smc ':
784 case 'SVQ1':
785 case 'SVQ3':
786 case 'tiff':
787 case 'v210':
788 case 'v216':
789 case 'v308':
790 case 'v408':
791 case 'v410':
792 case 'yuv2':
793 $info['fileformat'] = 'mp4';
794 $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
795 // http://www.getid3.org/phpBB3/viewtopic.php?t=1550
796 //if ((!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['width'])) && (empty($info['video']['resolution_x']) || empty($info['video']['resolution_y']) || (number_format($info['video']['resolution_x'], 6) != number_format(round($info['video']['resolution_x']), 6)) || (number_format($info['video']['resolution_y'], 6) != number_format(round($info['video']['resolution_y']), 6)))) { // ugly check for floating point numbers
797 if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
798 // assume that values stored here are more important than values stored in [tkhd] atom
799 $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width'];
800 $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height'];
801 $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
802 $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
803 }
804 break;
805
806 case 'qtvr':
807 $info['video']['dataformat'] = 'quicktimevr';
808 break;
809
810 case 'mp4a':
811 default:
812 $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
813 $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate'];
814 $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels'];
815 $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth'];
816 $info['audio']['codec'] = $info['quicktime']['audio']['codec'];
817 $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate'];
818 $info['audio']['channels'] = $info['quicktime']['audio']['channels'];
819 $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth'];
820 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
821 case 'raw ': // PCM
822 case 'alac': // Apple Lossless Audio Codec
823 $info['audio']['lossless'] = true;
824 break;
825 default:
826 $info['audio']['lossless'] = false;
827 break;
828 }
829 break;
830 }
831 break;
832
833 default:
834 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
835 case 'mp4s':
836 $info['fileformat'] = 'mp4';
837 break;
838
839 default:
840 // video atom
841 $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4));
842 $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4));
843 $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2));
844 $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2));
845 $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4));
846 $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4));
847 $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4));
848 $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2));
849 $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1));
850 $atom_structure['sample_description_table'][$i]['video_encoder_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 35, $atom_structure['sample_description_table'][$i]['video_encoder_name_len']);
851 $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2));
852 $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2));
853
854 $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
855 $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']);
856
857 if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
858 $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format'];
859 $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']);
860 $info['quicktime']['video']['codec'] = (($atom_structure['sample_description_table'][$i]['video_encoder_name_len'] > 0) ? $atom_structure['sample_description_table'][$i]['video_encoder_name'] : $atom_structure['sample_description_table'][$i]['data_format']);
861 $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'];
862 $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name'];
863
864 $info['video']['codec'] = $info['quicktime']['video']['codec'];
865 $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth'];
866 }
867 $info['video']['lossless'] = false;
868 $info['video']['pixel_aspect_ratio'] = (float) 1;
869 break;
870 }
871 break;
872 }
873 switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
874 case 'mp4a':
875 $info['audio']['dataformat'] = 'mp4';
876 $info['quicktime']['audio']['codec'] = 'mp4';
877 break;
878
879 case '3ivx':
880 case '3iv1':
881 case '3iv2':
882 $info['video']['dataformat'] = '3ivx';
883 break;
884
885 case 'xvid':
886 $info['video']['dataformat'] = 'xvid';
887 break;
888
889 case 'mp4v':
890 $info['video']['dataformat'] = 'mpeg4';
891 break;
892
893 case 'divx':
894 case 'div1':
895 case 'div2':
896 case 'div3':
897 case 'div4':
898 case 'div5':
899 case 'div6':
900 $info['video']['dataformat'] = 'divx';
901 break;
902
903 default:
904 // do nothing
905 break;
906 }
907 unset($atom_structure['sample_description_table'][$i]['data']);
908 }
909 break;
910
911
912 case 'stts': // Sample Table Time-to-Sample atom
913 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
914 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
915 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
916 $sttsEntriesDataOffset = 8;
917 //$FrameRateCalculatorArray = array();
918 $frames_count = 0;
919
920 $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']);
921 if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
922 $this->warning('QuickTime atom "stts" has '.$atom_structure['number_entries'].' but only scanning the first '.$max_stts_entries_to_scan.' entries due to limited PHP memory available ('.floor($atom_structure['number_entries'] / 1048576).'MB).');
923 }
924 for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
925 $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
926 $sttsEntriesDataOffset += 4;
927 $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
928 $sttsEntriesDataOffset += 4;
929
930 $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
931
932 // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM
933 //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) {
934 // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'];
935 // if ($stts_new_framerate <= 60) {
936 // // some atoms have durations of "1" giving a very large framerate, which probably is not right
937 // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate);
938 // }
939 //}
940 //
941 //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count'];
942 }
943 $info['quicktime']['stts_framecount'][] = $frames_count;
944 //$sttsFramesTotal = 0;
945 //$sttsSecondsTotal = 0;
946 //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) {
947 // if (($frames_per_second > 60) || ($frames_per_second < 1)) {
948 // // not video FPS information, probably audio information
949 // $sttsFramesTotal = 0;
950 // $sttsSecondsTotal = 0;
951 // break;
952 // }
953 // $sttsFramesTotal += $frame_count;
954 // $sttsSecondsTotal += $frame_count / $frames_per_second;
955 //}
956 //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) {
957 // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) {
958 // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal;
959 // }
960 //}
961 break;
962
963
964 case 'stss': // Sample Table Sync Sample (key frames) atom
965 if ($ParseAllPossibleAtoms) {
966 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
967 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
968 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
969 $stssEntriesDataOffset = 8;
970 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
971 $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
972 $stssEntriesDataOffset += 4;
973 }
974 }
975 break;
976
977
978 case 'stsc': // Sample Table Sample-to-Chunk atom
979 if ($ParseAllPossibleAtoms) {
980 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
981 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
982 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
983 $stscEntriesDataOffset = 8;
984 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
985 $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
986 $stscEntriesDataOffset += 4;
987 $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
988 $stscEntriesDataOffset += 4;
989 $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
990 $stscEntriesDataOffset += 4;
991 }
992 }
993 break;
994
995
996 case 'stsz': // Sample Table SiZe atom
997 if ($ParseAllPossibleAtoms) {
998 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
999 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1000 $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1001 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1002 $stszEntriesDataOffset = 12;
1003 if ($atom_structure['sample_size'] == 0) {
1004 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1005 $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
1006 $stszEntriesDataOffset += 4;
1007 }
1008 }
1009 }
1010 break;
1011
1012
1013 case 'stco': // Sample Table Chunk Offset atom
1014 if ($ParseAllPossibleAtoms) {
1015 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1016 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1017 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1018 $stcoEntriesDataOffset = 8;
1019 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1020 $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
1021 $stcoEntriesDataOffset += 4;
1022 }
1023 }
1024 break;
1025
1026
1027 case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files)
1028 if ($ParseAllPossibleAtoms) {
1029 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1030 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1031 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1032 $stcoEntriesDataOffset = 8;
1033 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1034 $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
1035 $stcoEntriesDataOffset += 8;
1036 }
1037 }
1038 break;
1039
1040
1041 case 'dref': // Data REFerence atom
1042 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1043 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1044 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1045 $drefDataOffset = 8;
1046 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1047 $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
1048 $drefDataOffset += 4;
1049 $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
1050 $drefDataOffset += 4;
1051 $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
1052 $drefDataOffset += 1;
1053 $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000
1054 $drefDataOffset += 3;
1055 $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
1056 $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
1057
1058 $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
1059 }
1060 break;
1061
1062
1063 case 'gmin': // base Media INformation atom
1064 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1065 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1066 $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
1067 $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
1068 $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
1069 $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
1070 $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2));
1071 $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2));
1072 break;
1073
1074
1075 case 'smhd': // Sound Media information HeaDer atom
1076 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1077 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1078 $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
1079 $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
1080 break;
1081
1082
1083 case 'vmhd': // Video Media information HeaDer atom
1084 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1085 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1086 $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2));
1087 $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2));
1088 $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2));
1089 $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2));
1090
1091 $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
1092 break;
1093
1094
1095 case 'hdlr': // HanDLeR reference atom
1096 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1097 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1098 $atom_structure['component_type'] = substr($atom_data, 4, 4);
1099 $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
1100 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
1101 $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1102 $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1103 $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24));
1104
1105 if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
1106 $info['video']['dataformat'] = 'quicktimevr';
1107 }
1108 break;
1109
1110
1111 case 'mdhd': // MeDia HeaDer atom
1112 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1113 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1114 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1115 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1116 $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1117 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1118 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2));
1119 $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2));
1120
1121 if ($atom_structure['time_scale'] == 0) {
1122 $this->error('Corrupt Quicktime file: mdhd.time_scale == zero');
1123 return false;
1124 }
1125 $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
1126
1127 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1128 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1129 $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1130 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']);
1131 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) {
1132 $info['comments']['language'][] = $atom_structure['language'];
1133 }
1134 break;
1135
1136
1137 case 'pnot': // Preview atom
1138 $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format"
1139 $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00
1140 $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT'
1141 $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01
1142
1143 $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']);
1144 break;
1145
1146
1147 case 'crgn': // Clipping ReGioN atom
1148 $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box,
1149 $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields
1150 $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region.
1151 break;
1152
1153
1154 case 'load': // track LOAD settings atom
1155 $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1156 $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1157 $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1158 $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1159
1160 $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
1161 $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
1162 break;
1163
1164
1165 case 'tmcd': // TiMe CoDe atom
1166 case 'chap': // CHAPter list atom
1167 case 'sync': // SYNChronization atom
1168 case 'scpt': // tranSCriPT atom
1169 case 'ssrc': // non-primary SouRCe atom
1170 for ($i = 0; $i < strlen($atom_data); $i += 4) {
1171 @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1172 }
1173 break;
1174
1175
1176 case 'elst': // Edit LiST atom
1177 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1178 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1179 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1180 for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
1181 $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
1182 $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
1183 $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
1184 }
1185 break;
1186
1187
1188 case 'kmat': // compressed MATte atom
1189 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1190 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000
1191 $atom_structure['matte_data_raw'] = substr($atom_data, 4);
1192 break;
1193
1194
1195 case 'ctab': // Color TABle atom
1196 $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000
1197 $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000
1198 $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1;
1199 for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
1200 $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
1201 $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
1202 $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
1203 $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
1204 }
1205 break;
1206
1207
1208 case 'mvhd': // MoVie HeaDer atom
1209 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1210 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1211 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1212 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1213 $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1214 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1215 $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4));
1216 $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2));
1217 $atom_structure['reserved'] = substr($atom_data, 26, 10);
1218 $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4));
1219 $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1220 $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4));
1221 $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4));
1222 $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1223 $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4));
1224 $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4));
1225 $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1226 $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4));
1227 $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4));
1228 $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4));
1229 $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4));
1230 $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4));
1231 $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4));
1232 $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4));
1233 $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4));
1234
1235 if ($atom_structure['time_scale'] == 0) {
1236 $this->error('Corrupt Quicktime file: mvhd.time_scale == zero');
1237 return false;
1238 }
1239 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1240 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1241 $info['quicktime']['time_scale'] = ((isset($info['quicktime']['time_scale']) && ($info['quicktime']['time_scale'] < 1000)) ? max($info['quicktime']['time_scale'], $atom_structure['time_scale']) : $atom_structure['time_scale']);
1242 $info['quicktime']['display_scale'] = $atom_structure['matrix_a'];
1243 $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1244 break;
1245
1246
1247 case 'tkhd': // TracK HeaDer atom
1248 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1249 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1250 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1251 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4));
1252 $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4));
1253 $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4));
1254 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4));
1255 $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8));
1256 $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2));
1257 $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2));
1258 $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2));
1259 $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2));
1260 // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html
1261 // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737
1262 $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4));
1263 $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4));
1264 $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4));
1265 $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4));
1266 $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4));
1267 $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4));
1268 $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4));
1269 $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4));
1270 $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4));
1271 $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4));
1272 $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4));
1273 $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001);
1274 $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002);
1275 $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
1276 $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
1277 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']);
1278 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']);
1279
1280 if ($atom_structure['flags']['enabled'] == 1) {
1281 if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) {
1282 $info['video']['resolution_x'] = $atom_structure['width'];
1283 $info['video']['resolution_y'] = $atom_structure['height'];
1284 }
1285 $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']);
1286 $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']);
1287 $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x'];
1288 $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y'];
1289 } else {
1290 // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295
1291 //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); }
1292 //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); }
1293 //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); }
1294 }
1295 break;
1296
1297
1298 case 'iods': // Initial Object DeScriptor atom
1299 // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h
1300 // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html
1301 $offset = 0;
1302 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1303 $offset += 1;
1304 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3));
1305 $offset += 3;
1306 $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1307 $offset += 1;
1308 $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1309 //$offset already adjusted by quicktime_read_mp4_descr_length()
1310 $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2));
1311 $offset += 2;
1312 $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1313 $offset += 1;
1314 $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1315 $offset += 1;
1316 $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1317 $offset += 1;
1318 $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1319 $offset += 1;
1320 $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1321 $offset += 1;
1322
1323 $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields
1324 for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
1325 $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1));
1326 $offset += 1;
1327 $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset);
1328 //$offset already adjusted by quicktime_read_mp4_descr_length()
1329 $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4));
1330 $offset += 4;
1331 }
1332
1333 $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']);
1334 $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']);
1335 break;
1336
1337 case 'ftyp': // FileTYPe (?) atom (for MP4 it seems)
1338 $atom_structure['signature'] = substr($atom_data, 0, 4);
1339 $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1340 $atom_structure['fourcc'] = substr($atom_data, 8, 4);
1341 break;
1342
1343 case 'mdat': // Media DATa atom
1344 // 'mdat' contains the actual data for the audio/video, possibly also subtitles
1345
1346 /* due to lack of known documentation, this is a kludge implementation. If you know of documentation on how mdat is properly structed, please send it to info@getid3.org */
1347
1348 // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?)
1349 $mdat_offset = 0;
1350 while (true) {
1351 if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
1352 $mdat_offset += 8;
1353 } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
1354 $mdat_offset += 8;
1355 } else {
1356 break;
1357 }
1358 }
1359
1360 // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field
1361 while (($mdat_offset < (strlen($atom_data) - 8))
1362 && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2)))
1363 && ($chapter_string_length < 1000)
1364 && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
1365 && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) {
1366 list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches;
1367 $mdat_offset += (2 + $chapter_string_length);
1368 @$info['quicktime']['comments']['chapters'][] = $chapter_string;
1369
1370 // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled)
1371 if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8
1372 $mdat_offset += 12;
1373 }
1374 }
1375
1376
1377 if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) {
1378
1379 $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8;
1380 $OldAVDataEnd = $info['avdataend'];
1381 $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size'];
1382
1383 $getid3_temp = new getID3();
1384 $getid3_temp->openfile($this->getid3->filename);
1385 $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
1386 $getid3_temp->info['avdataend'] = $info['avdataend'];
1387 $getid3_mp3 = new getid3_mp3($getid3_temp);
1388 if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
1389 $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1390 if (!empty($getid3_temp->info['warning'])) {
1391 foreach ($getid3_temp->info['warning'] as $value) {
1392 $this->warning($value);
1393 }
1394 }
1395 if (!empty($getid3_temp->info['mpeg'])) {
1396 $info['mpeg'] = $getid3_temp->info['mpeg'];
1397 if (isset($info['mpeg']['audio'])) {
1398 $info['audio']['dataformat'] = 'mp3';
1399 $info['audio']['codec'] = (!empty($info['mpeg']['audio']['encoder']) ? $info['mpeg']['audio']['encoder'] : (!empty($info['mpeg']['audio']['codec']) ? $info['mpeg']['audio']['codec'] : (!empty($info['mpeg']['audio']['LAME']) ? 'LAME' :'mp3')));
1400 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
1401 $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
1402 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
1403 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']);
1404 $info['bitrate'] = $info['audio']['bitrate'];
1405 }
1406 }
1407 }
1408 unset($getid3_mp3, $getid3_temp);
1409 $info['avdataend'] = $OldAVDataEnd;
1410 unset($OldAVDataEnd);
1411
1412 }
1413
1414 unset($mdat_offset, $chapter_string_length, $chapter_matches);
1415 break;
1416
1417 case 'free': // FREE space atom
1418 case 'skip': // SKIP atom
1419 case 'wide': // 64-bit expansion placeholder atom
1420 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all
1421
1422 // When writing QuickTime files, it is sometimes necessary to update an atom's size.
1423 // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom
1424 // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime
1425 // puts an 8-byte placeholder atom before any atoms it may have to update the size of.
1426 // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the
1427 // placeholder atom can be overwritten to obtain the necessary 8 extra bytes.
1428 // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ).
1429 break;
1430
1431
1432 case 'nsav': // NoSAVe atom
1433 // http://developer.apple.com/technotes/tn/tn2038.html
1434 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1435 break;
1436
1437 case 'ctyp': // Controller TYPe atom (seen on QTVR)
1438 // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt
1439 // some controller names are:
1440 // 0x00 + 'std' for linear movie
1441 // 'none' for no controls
1442 $atom_structure['ctyp'] = substr($atom_data, 0, 4);
1443 $info['quicktime']['controller'] = $atom_structure['ctyp'];
1444 switch ($atom_structure['ctyp']) {
1445 case 'qtvr':
1446 $info['video']['dataformat'] = 'quicktimevr';
1447 break;
1448 }
1449 break;
1450
1451 case 'pano': // PANOrama track (seen on QTVR)
1452 $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4));
1453 break;
1454
1455 case 'hint': // HINT track
1456 case 'hinf': //
1457 case 'hinv': //
1458 case 'hnti': //
1459 $info['quicktime']['hinting'] = true;
1460 break;
1461
1462 case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR)
1463 for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1464 $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4));
1465 }
1466 break;
1467
1468
1469 // Observed-but-not-handled atom types are just listed here to prevent warnings being generated
1470 case 'FXTC': // Something to do with Adobe After Effects (?)
1471 case 'PrmA':
1472 case 'code':
1473 case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html
1474 case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html
1475 // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838]
1476 // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html
1477 // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html
1478 case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1479 case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1480 case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1481 case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html
1482 //$atom_structure['data'] = $atom_data;
1483 break;
1484
1485 case "\xA9".'xyz': // GPS latitude+longitude+altitude
1486 $atom_structure['data'] = $atom_data;
1487 if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
1488 @list($all, $latitude, $longitude, $altitude) = $matches;
1489 $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude);
1490 $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude);
1491 if (!empty($altitude)) {
1492 $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude);
1493 }
1494 } else {
1495 $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.');
1496 }
1497 break;
1498
1499 case 'NCDT':
1500 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1501 // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
1502 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms);
1503 break;
1504 case 'NCTH': // Nikon Camera THumbnail image
1505 case 'NCVW': // Nikon Camera preVieW image
1506 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1507 if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
1508 $atom_structure['data'] = $atom_data;
1509 $atom_structure['image_mime'] = 'image/jpeg';
1510 $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
1511 $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']);
1512 }
1513 break;
1514 case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
1515 $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data);
1516 break;
1517 case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1518 case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html
1519 case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html
1520 $atom_structure['data'] = $atom_data;
1521 break;
1522
1523 case "\x00\x00\x00\x00":
1524 // some kind of metacontainer, may contain a big data dump such as:
1525 // mdta keys \005 mdtacom.apple.quicktime.make (mdtacom.apple.quicktime.creationdate ,mdtacom.apple.quicktime.location.ISO6709 $mdtacom.apple.quicktime.software !mdtacom.apple.quicktime.model ilst \01D \001 \015data \001DE\010Apple 0 \002 (data \001DE\0102011-05-11T17:54:04+0200 2 \003 *data \001DE\010+52.4936+013.3897+040.247/ \01D \004 \015data \001DE\0104.3.1 \005 \018data \001DE\010iPhone 4
1526 // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt
1527
1528 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1529 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1530 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1531 //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1532 break;
1533
1534 case 'meta': // METAdata atom
1535 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html
1536
1537 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1538 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1539 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms);
1540 break;
1541
1542 case 'data': // metaDATA atom
1543 static $metaDATAkey = 1; // real ugly, but so is the QuickTime structure that stores keys and values in different multinested locations that are hard to relate to each other
1544 // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data
1545 $atom_structure['language'] = substr($atom_data, 4 + 0, 2);
1546 $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2));
1547 $atom_structure['data'] = substr($atom_data, 4 + 4);
1548 $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++];
1549
1550 if ($atom_structure['key_name'] && $atom_structure['data']) {
1551 @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data'];
1552 }
1553 break;
1554
1555 case 'keys': // KEYS that may be present in the metadata atom.
1556 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21
1557 // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom.
1558 // This list is indexed starting with 1; 0 is a reserved index value. The metadata item keys atom is a full atom with an atom type of "keys".
1559 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1));
1560 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3));
1561 $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4));
1562 $keys_atom_offset = 8;
1563 for ($i = 1; $i <= $atom_structure['entry_count']; $i++) {
1564 $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4));
1565 $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4);
1566 $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8);
1567 $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace
1568
1569 $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value'];
1570 }
1571 break;
1572
1573 default:
1574 $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset);
1575 $atom_structure['data'] = $atom_data;
1576 break;
1577 }
1578 array_pop($atomHierarchy);
1579 return $atom_structure;
1580 }
1581
1582 public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) {
1583 //echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>';
1584 $atom_structure = false;
1585 $subatomoffset = 0;
1586 $subatomcounter = 0;
1587 if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) {
1588 return false;
1589 }
1590 while ($subatomoffset < strlen($atom_data)) {
1591 $subatomsize = getid3_lib::BigEndian2Int(substr($atom_data, $subatomoffset + 0, 4));
1592 $subatomname = substr($atom_data, $subatomoffset + 4, 4);
1593 $subatomdata = substr($atom_data, $subatomoffset + 8, $subatomsize - 8);
1594 if ($subatomsize == 0) {
1595 // Furthermore, for historical reasons the list of atoms is optionally
1596 // terminated by a 32-bit integer set to 0. If you are writing a program
1597 // to read user data atoms, you should allow for the terminating 0.
1598 if (strlen($atom_data) > 12) {
1599 $subatomoffset += 4;
1600 continue;
1601 }
1602 return $atom_structure;
1603 }
1604
1605 $atom_structure[$subatomcounter] = $this->QuicktimeParseAtom($subatomname, $subatomsize, $subatomdata, $baseoffset + $subatomoffset, $atomHierarchy, $ParseAllPossibleAtoms);
1606
1607 $subatomoffset += $subatomsize;
1608 $subatomcounter++;
1609 }
1610 return $atom_structure;
1611 }
1612
1613
1614 public function quicktime_read_mp4_descr_length($data, &$offset) {
1615 // http://libquicktime.sourcearchive.com/documentation/2:1.0.2plus-pdebian-2build1/esds_8c-source.html
1616 $num_bytes = 0;
1617 $length = 0;
1618 do {
1619 $b = ord(substr($data, $offset++, 1));
1620 $length = ($length << 7) | ($b & 0x7F);
1621 } while (($b & 0x80) && ($num_bytes++ < 4));
1622 return $length;
1623 }
1624
1625
1626 public function QuicktimeLanguageLookup($languageid) {
1627 // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-34353
1628 static $QuicktimeLanguageLookup = array();
1629 if (empty($QuicktimeLanguageLookup)) {
1630 $QuicktimeLanguageLookup[0] = 'English';
1631 $QuicktimeLanguageLookup[1] = 'French';
1632 $QuicktimeLanguageLookup[2] = 'German';
1633 $QuicktimeLanguageLookup[3] = 'Italian';
1634 $QuicktimeLanguageLookup[4] = 'Dutch';
1635 $QuicktimeLanguageLookup[5] = 'Swedish';
1636 $QuicktimeLanguageLookup[6] = 'Spanish';
1637 $QuicktimeLanguageLookup[7] = 'Danish';
1638 $QuicktimeLanguageLookup[8] = 'Portuguese';
1639 $QuicktimeLanguageLookup[9] = 'Norwegian';
1640 $QuicktimeLanguageLookup[10] = 'Hebrew';
1641 $QuicktimeLanguageLookup[11] = 'Japanese';
1642 $QuicktimeLanguageLookup[12] = 'Arabic';
1643 $QuicktimeLanguageLookup[13] = 'Finnish';
1644 $QuicktimeLanguageLookup[14] = 'Greek';
1645 $QuicktimeLanguageLookup[15] = 'Icelandic';
1646 $QuicktimeLanguageLookup[16] = 'Maltese';
1647 $QuicktimeLanguageLookup[17] = 'Turkish';
1648 $QuicktimeLanguageLookup[18] = 'Croatian';
1649 $QuicktimeLanguageLookup[19] = 'Chinese (Traditional)';
1650 $QuicktimeLanguageLookup[20] = 'Urdu';
1651 $QuicktimeLanguageLookup[21] = 'Hindi';
1652 $QuicktimeLanguageLookup[22] = 'Thai';
1653 $QuicktimeLanguageLookup[23] = 'Korean';
1654 $QuicktimeLanguageLookup[24] = 'Lithuanian';
1655 $QuicktimeLanguageLookup[25] = 'Polish';
1656 $QuicktimeLanguageLookup[26] = 'Hungarian';
1657 $QuicktimeLanguageLookup[27] = 'Estonian';
1658 $QuicktimeLanguageLookup[28] = 'Lettish';
1659 $QuicktimeLanguageLookup[28] = 'Latvian';
1660 $QuicktimeLanguageLookup[29] = 'Saamisk';
1661 $QuicktimeLanguageLookup[29] = 'Lappish';
1662 $QuicktimeLanguageLookup[30] = 'Faeroese';
1663 $QuicktimeLanguageLookup[31] = 'Farsi';
1664 $QuicktimeLanguageLookup[31] = 'Persian';
1665 $QuicktimeLanguageLookup[32] = 'Russian';
1666 $QuicktimeLanguageLookup[33] = 'Chinese (Simplified)';
1667 $QuicktimeLanguageLookup[34] = 'Flemish';
1668 $QuicktimeLanguageLookup[35] = 'Irish';
1669 $QuicktimeLanguageLookup[36] = 'Albanian';
1670 $QuicktimeLanguageLookup[37] = 'Romanian';
1671 $QuicktimeLanguageLookup[38] = 'Czech';
1672 $QuicktimeLanguageLookup[39] = 'Slovak';
1673 $QuicktimeLanguageLookup[40] = 'Slovenian';
1674 $QuicktimeLanguageLookup[41] = 'Yiddish';
1675 $QuicktimeLanguageLookup[42] = 'Serbian';
1676 $QuicktimeLanguageLookup[43] = 'Macedonian';
1677 $QuicktimeLanguageLookup[44] = 'Bulgarian';
1678 $QuicktimeLanguageLookup[45] = 'Ukrainian';
1679 $QuicktimeLanguageLookup[46] = 'Byelorussian';
1680 $QuicktimeLanguageLookup[47] = 'Uzbek';
1681 $QuicktimeLanguageLookup[48] = 'Kazakh';
1682 $QuicktimeLanguageLookup[49] = 'Azerbaijani';
1683 $QuicktimeLanguageLookup[50] = 'AzerbaijanAr';
1684 $QuicktimeLanguageLookup[51] = 'Armenian';
1685 $QuicktimeLanguageLookup[52] = 'Georgian';
1686 $QuicktimeLanguageLookup[53] = 'Moldavian';
1687 $QuicktimeLanguageLookup[54] = 'Kirghiz';
1688 $QuicktimeLanguageLookup[55] = 'Tajiki';
1689 $QuicktimeLanguageLookup[56] = 'Turkmen';
1690 $QuicktimeLanguageLookup[57] = 'Mongolian';
1691 $QuicktimeLanguageLookup[58] = 'MongolianCyr';
1692 $QuicktimeLanguageLookup[59] = 'Pashto';
1693 $QuicktimeLanguageLookup[60] = 'Kurdish';
1694 $QuicktimeLanguageLookup[61] = 'Kashmiri';
1695 $QuicktimeLanguageLookup[62] = 'Sindhi';
1696 $QuicktimeLanguageLookup[63] = 'Tibetan';
1697 $QuicktimeLanguageLookup[64] = 'Nepali';
1698 $QuicktimeLanguageLookup[65] = 'Sanskrit';
1699 $QuicktimeLanguageLookup[66] = 'Marathi';
1700 $QuicktimeLanguageLookup[67] = 'Bengali';
1701 $QuicktimeLanguageLookup[68] = 'Assamese';
1702 $QuicktimeLanguageLookup[69] = 'Gujarati';
1703 $QuicktimeLanguageLookup[70] = 'Punjabi';
1704 $QuicktimeLanguageLookup[71] = 'Oriya';
1705 $QuicktimeLanguageLookup[72] = 'Malayalam';
1706 $QuicktimeLanguageLookup[73] = 'Kannada';
1707 $QuicktimeLanguageLookup[74] = 'Tamil';
1708 $QuicktimeLanguageLookup[75] = 'Telugu';
1709 $QuicktimeLanguageLookup[76] = 'Sinhalese';
1710 $QuicktimeLanguageLookup[77] = 'Burmese';
1711 $QuicktimeLanguageLookup[78] = 'Khmer';
1712 $QuicktimeLanguageLookup[79] = 'Lao';
1713 $QuicktimeLanguageLookup[80] = 'Vietnamese';
1714 $QuicktimeLanguageLookup[81] = 'Indonesian';
1715 $QuicktimeLanguageLookup[82] = 'Tagalog';
1716 $QuicktimeLanguageLookup[83] = 'MalayRoman';
1717 $QuicktimeLanguageLookup[84] = 'MalayArabic';
1718 $QuicktimeLanguageLookup[85] = 'Amharic';
1719 $QuicktimeLanguageLookup[86] = 'Tigrinya';
1720 $QuicktimeLanguageLookup[87] = 'Galla';
1721 $QuicktimeLanguageLookup[87] = 'Oromo';
1722 $QuicktimeLanguageLookup[88] = 'Somali';
1723 $QuicktimeLanguageLookup[89] = 'Swahili';
1724 $QuicktimeLanguageLookup[90] = 'Ruanda';
1725 $QuicktimeLanguageLookup[91] = 'Rundi';
1726 $QuicktimeLanguageLookup[92] = 'Chewa';
1727 $QuicktimeLanguageLookup[93] = 'Malagasy';
1728 $QuicktimeLanguageLookup[94] = 'Esperanto';
1729 $QuicktimeLanguageLookup[128] = 'Welsh';
1730 $QuicktimeLanguageLookup[129] = 'Basque';
1731 $QuicktimeLanguageLookup[130] = 'Catalan';
1732 $QuicktimeLanguageLookup[131] = 'Latin';
1733 $QuicktimeLanguageLookup[132] = 'Quechua';
1734 $QuicktimeLanguageLookup[133] = 'Guarani';
1735 $QuicktimeLanguageLookup[134] = 'Aymara';
1736 $QuicktimeLanguageLookup[135] = 'Tatar';
1737 $QuicktimeLanguageLookup[136] = 'Uighur';
1738 $QuicktimeLanguageLookup[137] = 'Dzongkha';
1739 $QuicktimeLanguageLookup[138] = 'JavaneseRom';
1740 $QuicktimeLanguageLookup[32767] = 'Unspecified';
1741 }
1742 if (($languageid > 138) && ($languageid < 32767)) {
1743 /*
1744 ISO Language Codes - http://www.loc.gov/standards/iso639-2/php/code_list.php
1745 Because the language codes specified by ISO 639-2/T are three characters long, they must be packed to fit into a 16-bit field.
1746 The packing algorithm must map each of the three characters, which are always lowercase, into a 5-bit integer and then concatenate
1747 these integers into the least significant 15 bits of a 16-bit integer, leaving the 16-bit integer's most significant bit set to zero.
1748
1749 One algorithm for performing this packing is to treat each ISO character as a 16-bit integer. Subtract 0x60 from the first character
1750 and multiply by 2^10 (0x400), subtract 0x60 from the second character and multiply by 2^5 (0x20), subtract 0x60 from the third character,
1751 and add the three 16-bit values. This will result in a single 16-bit value with the three codes correctly packed into the 15 least
1752 significant bits and the most significant bit set to zero.
1753 */
1754 $iso_language_id = '';
1755 $iso_language_id .= chr((($languageid & 0x7C00) >> 10) + 0x60);
1756 $iso_language_id .= chr((($languageid & 0x03E0) >> 5) + 0x60);
1757 $iso_language_id .= chr((($languageid & 0x001F) >> 0) + 0x60);
1758 $QuicktimeLanguageLookup[$languageid] = getid3_id3v2::LanguageLookup($iso_language_id);
1759 }
1760 return (isset($QuicktimeLanguageLookup[$languageid]) ? $QuicktimeLanguageLookup[$languageid] : 'invalid');
1761 }
1762
1763 public function QuicktimeVideoCodecLookup($codecid) {
1764 static $QuicktimeVideoCodecLookup = array();
1765 if (empty($QuicktimeVideoCodecLookup)) {
1766 $QuicktimeVideoCodecLookup['.SGI'] = 'SGI';
1767 $QuicktimeVideoCodecLookup['3IV1'] = '3ivx MPEG-4 v1';
1768 $QuicktimeVideoCodecLookup['3IV2'] = '3ivx MPEG-4 v2';
1769 $QuicktimeVideoCodecLookup['3IVX'] = '3ivx MPEG-4';
1770 $QuicktimeVideoCodecLookup['8BPS'] = 'Planar RGB';
1771 $QuicktimeVideoCodecLookup['avc1'] = 'H.264/MPEG-4 AVC';
1772 $QuicktimeVideoCodecLookup['avr '] = 'AVR-JPEG';
1773 $QuicktimeVideoCodecLookup['b16g'] = '16Gray';
1774 $QuicktimeVideoCodecLookup['b32a'] = '32AlphaGray';
1775 $QuicktimeVideoCodecLookup['b48r'] = '48RGB';
1776 $QuicktimeVideoCodecLookup['b64a'] = '64ARGB';
1777 $QuicktimeVideoCodecLookup['base'] = 'Base';
1778 $QuicktimeVideoCodecLookup['clou'] = 'Cloud';
1779 $QuicktimeVideoCodecLookup['cmyk'] = 'CMYK';
1780 $QuicktimeVideoCodecLookup['cvid'] = 'Cinepak';
1781 $QuicktimeVideoCodecLookup['dmb1'] = 'OpenDML JPEG';
1782 $QuicktimeVideoCodecLookup['dvc '] = 'DVC-NTSC';
1783 $QuicktimeVideoCodecLookup['dvcp'] = 'DVC-PAL';
1784 $QuicktimeVideoCodecLookup['dvpn'] = 'DVCPro-NTSC';
1785 $QuicktimeVideoCodecLookup['dvpp'] = 'DVCPro-PAL';
1786 $QuicktimeVideoCodecLookup['fire'] = 'Fire';
1787 $QuicktimeVideoCodecLookup['flic'] = 'FLC';
1788 $QuicktimeVideoCodecLookup['gif '] = 'GIF';
1789 $QuicktimeVideoCodecLookup['h261'] = 'H261';
1790 $QuicktimeVideoCodecLookup['h263'] = 'H263';
1791 $QuicktimeVideoCodecLookup['IV41'] = 'Indeo4';
1792 $QuicktimeVideoCodecLookup['jpeg'] = 'JPEG';
1793 $QuicktimeVideoCodecLookup['kpcd'] = 'PhotoCD';
1794 $QuicktimeVideoCodecLookup['mjpa'] = 'Motion JPEG-A';
1795 $QuicktimeVideoCodecLookup['mjpb'] = 'Motion JPEG-B';
1796 $QuicktimeVideoCodecLookup['msvc'] = 'Microsoft Video1';
1797 $QuicktimeVideoCodecLookup['myuv'] = 'MPEG YUV420';
1798 $QuicktimeVideoCodecLookup['path'] = 'Vector';
1799 $QuicktimeVideoCodecLookup['png '] = 'PNG';
1800 $QuicktimeVideoCodecLookup['PNTG'] = 'MacPaint';
1801 $QuicktimeVideoCodecLookup['qdgx'] = 'QuickDrawGX';
1802 $QuicktimeVideoCodecLookup['qdrw'] = 'QuickDraw';
1803 $QuicktimeVideoCodecLookup['raw '] = 'RAW';
1804 $QuicktimeVideoCodecLookup['ripl'] = 'WaterRipple';
1805 $QuicktimeVideoCodecLookup['rpza'] = 'Video';
1806 $QuicktimeVideoCodecLookup['smc '] = 'Graphics';
1807 $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 1';
1808 $QuicktimeVideoCodecLookup['SVQ1'] = 'Sorenson Video 3';
1809 $QuicktimeVideoCodecLookup['syv9'] = 'Sorenson YUV9';
1810 $QuicktimeVideoCodecLookup['tga '] = 'Targa';
1811 $QuicktimeVideoCodecLookup['tiff'] = 'TIFF';
1812 $QuicktimeVideoCodecLookup['WRAW'] = 'Windows RAW';
1813 $QuicktimeVideoCodecLookup['WRLE'] = 'BMP';
1814 $QuicktimeVideoCodecLookup['y420'] = 'YUV420';
1815 $QuicktimeVideoCodecLookup['yuv2'] = 'ComponentVideo';
1816 $QuicktimeVideoCodecLookup['yuvs'] = 'ComponentVideoUnsigned';
1817 $QuicktimeVideoCodecLookup['yuvu'] = 'ComponentVideoSigned';
1818 }
1819 return (isset($QuicktimeVideoCodecLookup[$codecid]) ? $QuicktimeVideoCodecLookup[$codecid] : '');
1820 }
1821
1822 public function QuicktimeAudioCodecLookup($codecid) {
1823 static $QuicktimeAudioCodecLookup = array();
1824 if (empty($QuicktimeAudioCodecLookup)) {
1825 $QuicktimeAudioCodecLookup['.mp3'] = 'Fraunhofer MPEG Layer-III alias';
1826 $QuicktimeAudioCodecLookup['aac '] = 'ISO/IEC 14496-3 AAC';
1827 $QuicktimeAudioCodecLookup['agsm'] = 'Apple GSM 10:1';
1828 $QuicktimeAudioCodecLookup['alac'] = 'Apple Lossless Audio Codec';
1829 $QuicktimeAudioCodecLookup['alaw'] = 'A-law 2:1';
1830 $QuicktimeAudioCodecLookup['conv'] = 'Sample Format';
1831 $QuicktimeAudioCodecLookup['dvca'] = 'DV';
1832 $QuicktimeAudioCodecLookup['dvi '] = 'DV 4:1';
1833 $QuicktimeAudioCodecLookup['eqal'] = 'Frequency Equalizer';
1834 $QuicktimeAudioCodecLookup['fl32'] = '32-bit Floating Point';
1835 $QuicktimeAudioCodecLookup['fl64'] = '64-bit Floating Point';
1836 $QuicktimeAudioCodecLookup['ima4'] = 'Interactive Multimedia Association 4:1';
1837 $QuicktimeAudioCodecLookup['in24'] = '24-bit Integer';
1838 $QuicktimeAudioCodecLookup['in32'] = '32-bit Integer';
1839 $QuicktimeAudioCodecLookup['lpc '] = 'LPC 23:1';
1840 $QuicktimeAudioCodecLookup['MAC3'] = 'Macintosh Audio Compression/Expansion (MACE) 3:1';
1841 $QuicktimeAudioCodecLookup['MAC6'] = 'Macintosh Audio Compression/Expansion (MACE) 6:1';
1842 $QuicktimeAudioCodecLookup['mixb'] = '8-bit Mixer';
1843 $QuicktimeAudioCodecLookup['mixw'] = '16-bit Mixer';
1844 $QuicktimeAudioCodecLookup['mp4a'] = 'ISO/IEC 14496-3 AAC';
1845 $QuicktimeAudioCodecLookup['MS'."\x00\x02"] = 'Microsoft ADPCM';
1846 $QuicktimeAudioCodecLookup['MS'."\x00\x11"] = 'DV IMA';
1847 $QuicktimeAudioCodecLookup['MS'."\x00\x55"] = 'Fraunhofer MPEG Layer III';
1848 $QuicktimeAudioCodecLookup['NONE'] = 'No Encoding';
1849 $QuicktimeAudioCodecLookup['Qclp'] = 'Qualcomm PureVoice';
1850 $QuicktimeAudioCodecLookup['QDM2'] = 'QDesign Music 2';
1851 $QuicktimeAudioCodecLookup['QDMC'] = 'QDesign Music 1';
1852 $QuicktimeAudioCodecLookup['ratb'] = '8-bit Rate';
1853 $QuicktimeAudioCodecLookup['ratw'] = '16-bit Rate';
1854 $QuicktimeAudioCodecLookup['raw '] = 'raw PCM';
1855 $QuicktimeAudioCodecLookup['sour'] = 'Sound Source';
1856 $QuicktimeAudioCodecLookup['sowt'] = 'signed/two\'s complement (Little Endian)';
1857 $QuicktimeAudioCodecLookup['str1'] = 'Iomega MPEG layer II';
1858 $QuicktimeAudioCodecLookup['str2'] = 'Iomega MPEG *layer II';
1859 $QuicktimeAudioCodecLookup['str3'] = 'Iomega MPEG **layer II';
1860 $QuicktimeAudioCodecLookup['str4'] = 'Iomega MPEG ***layer II';
1861 $QuicktimeAudioCodecLookup['twos'] = 'signed/two\'s complement (Big Endian)';
1862 $QuicktimeAudioCodecLookup['ulaw'] = 'mu-law 2:1';
1863 }
1864 return (isset($QuicktimeAudioCodecLookup[$codecid]) ? $QuicktimeAudioCodecLookup[$codecid] : '');
1865 }
1866
1867 public function QuicktimeDCOMLookup($compressionid) {
1868 static $QuicktimeDCOMLookup = array();
1869 if (empty($QuicktimeDCOMLookup)) {
1870 $QuicktimeDCOMLookup['zlib'] = 'ZLib Deflate';
1871 $QuicktimeDCOMLookup['adec'] = 'Apple Compression';
1872 }
1873 return (isset($QuicktimeDCOMLookup[$compressionid]) ? $QuicktimeDCOMLookup[$compressionid] : '');
1874 }
1875
1876 public function QuicktimeColorNameLookup($colordepthid) {
1877 static $QuicktimeColorNameLookup = array();
1878 if (empty($QuicktimeColorNameLookup)) {
1879 $QuicktimeColorNameLookup[1] = '2-color (monochrome)';
1880 $QuicktimeColorNameLookup[2] = '4-color';
1881 $QuicktimeColorNameLookup[4] = '16-color';
1882 $QuicktimeColorNameLookup[8] = '256-color';
1883 $QuicktimeColorNameLookup[16] = 'thousands (16-bit color)';
1884 $QuicktimeColorNameLookup[24] = 'millions (24-bit color)';
1885 $QuicktimeColorNameLookup[32] = 'millions+ (32-bit color)';
1886 $QuicktimeColorNameLookup[33] = 'black & white';
1887 $QuicktimeColorNameLookup[34] = '4-gray';
1888 $QuicktimeColorNameLookup[36] = '16-gray';
1889 $QuicktimeColorNameLookup[40] = '256-gray';
1890 }
1891 return (isset($QuicktimeColorNameLookup[$colordepthid]) ? $QuicktimeColorNameLookup[$colordepthid] : 'invalid');
1892 }
1893
1894 public function QuicktimeSTIKLookup($stik) {
1895 static $QuicktimeSTIKLookup = array();
1896 if (empty($QuicktimeSTIKLookup)) {
1897 $QuicktimeSTIKLookup[0] = 'Movie';
1898 $QuicktimeSTIKLookup[1] = 'Normal';
1899 $QuicktimeSTIKLookup[2] = 'Audiobook';
1900 $QuicktimeSTIKLookup[5] = 'Whacked Bookmark';
1901 $QuicktimeSTIKLookup[6] = 'Music Video';
1902 $QuicktimeSTIKLookup[9] = 'Short Film';
1903 $QuicktimeSTIKLookup[10] = 'TV Show';
1904 $QuicktimeSTIKLookup[11] = 'Booklet';
1905 $QuicktimeSTIKLookup[14] = 'Ringtone';
1906 $QuicktimeSTIKLookup[21] = 'Podcast';
1907 }
1908 return (isset($QuicktimeSTIKLookup[$stik]) ? $QuicktimeSTIKLookup[$stik] : 'invalid');
1909 }
1910
1911 public function QuicktimeIODSaudioProfileName($audio_profile_id) {
1912 static $QuicktimeIODSaudioProfileNameLookup = array();
1913 if (empty($QuicktimeIODSaudioProfileNameLookup)) {
1914 $QuicktimeIODSaudioProfileNameLookup = array(
1915 0x00 => 'ISO Reserved (0x00)',
1916 0x01 => 'Main Audio Profile @ Level 1',
1917 0x02 => 'Main Audio Profile @ Level 2',
1918 0x03 => 'Main Audio Profile @ Level 3',
1919 0x04 => 'Main Audio Profile @ Level 4',
1920 0x05 => 'Scalable Audio Profile @ Level 1',
1921 0x06 => 'Scalable Audio Profile @ Level 2',
1922 0x07 => 'Scalable Audio Profile @ Level 3',
1923 0x08 => 'Scalable Audio Profile @ Level 4',
1924 0x09 => 'Speech Audio Profile @ Level 1',
1925 0x0A => 'Speech Audio Profile @ Level 2',
1926 0x0B => 'Synthetic Audio Profile @ Level 1',
1927 0x0C => 'Synthetic Audio Profile @ Level 2',
1928 0x0D => 'Synthetic Audio Profile @ Level 3',
1929 0x0E => 'High Quality Audio Profile @ Level 1',
1930 0x0F => 'High Quality Audio Profile @ Level 2',
1931 0x10 => 'High Quality Audio Profile @ Level 3',
1932 0x11 => 'High Quality Audio Profile @ Level 4',
1933 0x12 => 'High Quality Audio Profile @ Level 5',
1934 0x13 => 'High Quality Audio Profile @ Level 6',
1935 0x14 => 'High Quality Audio Profile @ Level 7',
1936 0x15 => 'High Quality Audio Profile @ Level 8',
1937 0x16 => 'Low Delay Audio Profile @ Level 1',
1938 0x17 => 'Low Delay Audio Profile @ Level 2',
1939 0x18 => 'Low Delay Audio Profile @ Level 3',
1940 0x19 => 'Low Delay Audio Profile @ Level 4',
1941 0x1A => 'Low Delay Audio Profile @ Level 5',
1942 0x1B => 'Low Delay Audio Profile @ Level 6',
1943 0x1C => 'Low Delay Audio Profile @ Level 7',
1944 0x1D => 'Low Delay Audio Profile @ Level 8',
1945 0x1E => 'Natural Audio Profile @ Level 1',
1946 0x1F => 'Natural Audio Profile @ Level 2',
1947 0x20 => 'Natural Audio Profile @ Level 3',
1948 0x21 => 'Natural Audio Profile @ Level 4',
1949 0x22 => 'Mobile Audio Internetworking Profile @ Level 1',
1950 0x23 => 'Mobile Audio Internetworking Profile @ Level 2',
1951 0x24 => 'Mobile Audio Internetworking Profile @ Level 3',
1952 0x25 => 'Mobile Audio Internetworking Profile @ Level 4',
1953 0x26 => 'Mobile Audio Internetworking Profile @ Level 5',
1954 0x27 => 'Mobile Audio Internetworking Profile @ Level 6',
1955 0x28 => 'AAC Profile @ Level 1',
1956 0x29 => 'AAC Profile @ Level 2',
1957 0x2A => 'AAC Profile @ Level 4',
1958 0x2B => 'AAC Profile @ Level 5',
1959 0x2C => 'High Efficiency AAC Profile @ Level 2',
1960 0x2D => 'High Efficiency AAC Profile @ Level 3',
1961 0x2E => 'High Efficiency AAC Profile @ Level 4',
1962 0x2F => 'High Efficiency AAC Profile @ Level 5',
1963 0xFE => 'Not part of MPEG-4 audio profiles',
1964 0xFF => 'No audio capability required',
1965 );
1966 }
1967 return (isset($QuicktimeIODSaudioProfileNameLookup[$audio_profile_id]) ? $QuicktimeIODSaudioProfileNameLookup[$audio_profile_id] : 'ISO Reserved / User Private');
1968 }
1969
1970
1971 public function QuicktimeIODSvideoProfileName($video_profile_id) {
1972 static $QuicktimeIODSvideoProfileNameLookup = array();
1973 if (empty($QuicktimeIODSvideoProfileNameLookup)) {
1974 $QuicktimeIODSvideoProfileNameLookup = array(
1975 0x00 => 'Reserved (0x00) Profile',
1976 0x01 => 'Simple Profile @ Level 1',
1977 0x02 => 'Simple Profile @ Level 2',
1978 0x03 => 'Simple Profile @ Level 3',
1979 0x08 => 'Simple Profile @ Level 0',
1980 0x10 => 'Simple Scalable Profile @ Level 0',
1981 0x11 => 'Simple Scalable Profile @ Level 1',
1982 0x12 => 'Simple Scalable Profile @ Level 2',
1983 0x15 => 'AVC/H264 Profile',
1984 0x21 => 'Core Profile @ Level 1',
1985 0x22 => 'Core Profile @ Level 2',
1986 0x32 => 'Main Profile @ Level 2',
1987 0x33 => 'Main Profile @ Level 3',
1988 0x34 => 'Main Profile @ Level 4',
1989 0x42 => 'N-bit Profile @ Level 2',
1990 0x51 => 'Scalable Texture Profile @ Level 1',
1991 0x61 => 'Simple Face Animation Profile @ Level 1',
1992 0x62 => 'Simple Face Animation Profile @ Level 2',
1993 0x63 => 'Simple FBA Profile @ Level 1',
1994 0x64 => 'Simple FBA Profile @ Level 2',
1995 0x71 => 'Basic Animated Texture Profile @ Level 1',
1996 0x72 => 'Basic Animated Texture Profile @ Level 2',
1997 0x81 => 'Hybrid Profile @ Level 1',
1998 0x82 => 'Hybrid Profile @ Level 2',
1999 0x91 => 'Advanced Real Time Simple Profile @ Level 1',
2000 0x92 => 'Advanced Real Time Simple Profile @ Level 2',
2001 0x93 => 'Advanced Real Time Simple Profile @ Level 3',
2002 0x94 => 'Advanced Real Time Simple Profile @ Level 4',
2003 0xA1 => 'Core Scalable Profile @ Level1',
2004 0xA2 => 'Core Scalable Profile @ Level2',
2005 0xA3 => 'Core Scalable Profile @ Level3',
2006 0xB1 => 'Advanced Coding Efficiency Profile @ Level 1',
2007 0xB2 => 'Advanced Coding Efficiency Profile @ Level 2',
2008 0xB3 => 'Advanced Coding Efficiency Profile @ Level 3',
2009 0xB4 => 'Advanced Coding Efficiency Profile @ Level 4',
2010 0xC1 => 'Advanced Core Profile @ Level 1',
2011 0xC2 => 'Advanced Core Profile @ Level 2',
2012 0xD1 => 'Advanced Scalable Texture @ Level1',
2013 0xD2 => 'Advanced Scalable Texture @ Level2',
2014 0xE1 => 'Simple Studio Profile @ Level 1',
2015 0xE2 => 'Simple Studio Profile @ Level 2',
2016 0xE3 => 'Simple Studio Profile @ Level 3',
2017 0xE4 => 'Simple Studio Profile @ Level 4',
2018 0xE5 => 'Core Studio Profile @ Level 1',
2019 0xE6 => 'Core Studio Profile @ Level 2',
2020 0xE7 => 'Core Studio Profile @ Level 3',
2021 0xE8 => 'Core Studio Profile @ Level 4',
2022 0xF0 => 'Advanced Simple Profile @ Level 0',
2023 0xF1 => 'Advanced Simple Profile @ Level 1',
2024 0xF2 => 'Advanced Simple Profile @ Level 2',
2025 0xF3 => 'Advanced Simple Profile @ Level 3',
2026 0xF4 => 'Advanced Simple Profile @ Level 4',
2027 0xF5 => 'Advanced Simple Profile @ Level 5',
2028 0xF7 => 'Advanced Simple Profile @ Level 3b',
2029 0xF8 => 'Fine Granularity Scalable Profile @ Level 0',
2030 0xF9 => 'Fine Granularity Scalable Profile @ Level 1',
2031 0xFA => 'Fine Granularity Scalable Profile @ Level 2',
2032 0xFB => 'Fine Granularity Scalable Profile @ Level 3',
2033 0xFC => 'Fine Granularity Scalable Profile @ Level 4',
2034 0xFD => 'Fine Granularity Scalable Profile @ Level 5',
2035 0xFE => 'Not part of MPEG-4 Visual profiles',
2036 0xFF => 'No visual capability required',
2037 );
2038 }
2039 return (isset($QuicktimeIODSvideoProfileNameLookup[$video_profile_id]) ? $QuicktimeIODSvideoProfileNameLookup[$video_profile_id] : 'ISO Reserved Profile');
2040 }
2041
2042
2043 public function QuicktimeContentRatingLookup($rtng) {
2044 static $QuicktimeContentRatingLookup = array();
2045 if (empty($QuicktimeContentRatingLookup)) {
2046 $QuicktimeContentRatingLookup[0] = 'None';
2047 $QuicktimeContentRatingLookup[2] = 'Clean';
2048 $QuicktimeContentRatingLookup[4] = 'Explicit';
2049 }
2050 return (isset($QuicktimeContentRatingLookup[$rtng]) ? $QuicktimeContentRatingLookup[$rtng] : 'invalid');
2051 }
2052
2053 public function QuicktimeStoreAccountTypeLookup($akid) {
2054 static $QuicktimeStoreAccountTypeLookup = array();
2055 if (empty($QuicktimeStoreAccountTypeLookup)) {
2056 $QuicktimeStoreAccountTypeLookup[0] = 'iTunes';
2057 $QuicktimeStoreAccountTypeLookup[1] = 'AOL';
2058 }
2059 return (isset($QuicktimeStoreAccountTypeLookup[$akid]) ? $QuicktimeStoreAccountTypeLookup[$akid] : 'invalid');
2060 }
2061
2062 public function QuicktimeStoreFrontCodeLookup($sfid) {
2063 static $QuicktimeStoreFrontCodeLookup = array();
2064 if (empty($QuicktimeStoreFrontCodeLookup)) {
2065 $QuicktimeStoreFrontCodeLookup[143460] = 'Australia';
2066 $QuicktimeStoreFrontCodeLookup[143445] = 'Austria';
2067 $QuicktimeStoreFrontCodeLookup[143446] = 'Belgium';
2068 $QuicktimeStoreFrontCodeLookup[143455] = 'Canada';
2069 $QuicktimeStoreFrontCodeLookup[143458] = 'Denmark';
2070 $QuicktimeStoreFrontCodeLookup[143447] = 'Finland';
2071 $QuicktimeStoreFrontCodeLookup[143442] = 'France';
2072 $QuicktimeStoreFrontCodeLookup[143443] = 'Germany';
2073 $QuicktimeStoreFrontCodeLookup[143448] = 'Greece';
2074 $QuicktimeStoreFrontCodeLookup[143449] = 'Ireland';
2075 $QuicktimeStoreFrontCodeLookup[143450] = 'Italy';
2076 $QuicktimeStoreFrontCodeLookup[143462] = 'Japan';
2077 $QuicktimeStoreFrontCodeLookup[143451] = 'Luxembourg';
2078 $QuicktimeStoreFrontCodeLookup[143452] = 'Netherlands';
2079 $QuicktimeStoreFrontCodeLookup[143461] = 'New Zealand';
2080 $QuicktimeStoreFrontCodeLookup[143457] = 'Norway';
2081 $QuicktimeStoreFrontCodeLookup[143453] = 'Portugal';
2082 $QuicktimeStoreFrontCodeLookup[143454] = 'Spain';
2083 $QuicktimeStoreFrontCodeLookup[143456] = 'Sweden';
2084 $QuicktimeStoreFrontCodeLookup[143459] = 'Switzerland';
2085 $QuicktimeStoreFrontCodeLookup[143444] = 'United Kingdom';
2086 $QuicktimeStoreFrontCodeLookup[143441] = 'United States';
2087 }
2088 return (isset($QuicktimeStoreFrontCodeLookup[$sfid]) ? $QuicktimeStoreFrontCodeLookup[$sfid] : 'invalid');
2089 }
2090
2091 public function QuicktimeParseNikonNCTG($atom_data) {
2092 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
2093 // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
2094 // Data is stored as records of:
2095 // * 4 bytes record type
2096 // * 2 bytes size of data field type:
2097 // 0x0001 = flag (size field *= 1-byte)
2098 // 0x0002 = char (size field *= 1-byte)
2099 // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
2100 // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
2101 // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
2102 // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
2103 // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
2104 // * 2 bytes data size field
2105 // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
2106 // all integers are stored BigEndian
2107
2108 $NCTGtagName = array(
2109 0x00000001 => 'Make',
2110 0x00000002 => 'Model',
2111 0x00000003 => 'Software',
2112 0x00000011 => 'CreateDate',
2113 0x00000012 => 'DateTimeOriginal',
2114 0x00000013 => 'FrameCount',
2115 0x00000016 => 'FrameRate',
2116 0x00000022 => 'FrameWidth',
2117 0x00000023 => 'FrameHeight',
2118 0x00000032 => 'AudioChannels',
2119 0x00000033 => 'AudioBitsPerSample',
2120 0x00000034 => 'AudioSampleRate',
2121 0x02000001 => 'MakerNoteVersion',
2122 0x02000005 => 'WhiteBalance',
2123 0x0200000b => 'WhiteBalanceFineTune',
2124 0x0200001e => 'ColorSpace',
2125 0x02000023 => 'PictureControlData',
2126 0x02000024 => 'WorldTime',
2127 0x02000032 => 'UnknownInfo',
2128 0x02000083 => 'LensType',
2129 0x02000084 => 'Lens',
2130 );
2131
2132 $offset = 0;
2133 $datalength = strlen($atom_data);
2134 $parsed = array();
2135 while ($offset < $datalength) {
2136 //echo getid3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
2137 $record_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); $offset += 4;
2138 $data_size_type = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
2139 $data_size = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); $offset += 2;
2140 switch ($data_size_type) {
2141 case 0x0001: // 0x0001 = flag (size field *= 1-byte)
2142 $data = getid3_lib::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
2143 $offset += ($data_size * 1);
2144 break;
2145 case 0x0002: // 0x0002 = char (size field *= 1-byte)
2146 $data = substr($atom_data, $offset, $data_size * 1);
2147 $offset += ($data_size * 1);
2148 $data = rtrim($data, "\x00");
2149 break;
2150 case 0x0003: // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
2151 $data = '';
2152 for ($i = $data_size - 1; $i >= 0; $i--) {
2153 $data .= substr($atom_data, $offset + ($i * 2), 2);
2154 }
2155 $data = getid3_lib::BigEndian2Int($data);
2156 $offset += ($data_size * 2);
2157 break;
2158 case 0x0004: // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
2159 $data = '';
2160 for ($i = $data_size - 1; $i >= 0; $i--) {
2161 $data .= substr($atom_data, $offset + ($i * 4), 4);
2162 }
2163 $data = getid3_lib::BigEndian2Int($data);
2164 $offset += ($data_size * 4);
2165 break;
2166 case 0x0005: // 0x0005 = float (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
2167 $data = array();
2168 for ($i = 0; $i < $data_size; $i++) {
2169 $numerator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 0, 4));
2170 $denomninator = getid3_lib::BigEndian2Int(substr($atom_data, $offset + ($i * 8) + 4, 4));
2171 if ($denomninator == 0) {
2172 $data[$i] = false;
2173 } else {
2174 $data[$i] = (double) $numerator / $denomninator;
2175 }
2176 }
2177 $offset += (8 * $data_size);
2178 if (count($data) == 1) {
2179 $data = $data[0];
2180 }
2181 break;
2182 case 0x0007: // 0x0007 = bytes (size field *= 1-byte), values are stored as ??????
2183 $data = substr($atom_data, $offset, $data_size * 1);
2184 $offset += ($data_size * 1);
2185 break;
2186 case 0x0008: // 0x0008 = ????? (size field *= 2-byte), values are stored as ??????
2187 $data = substr($atom_data, $offset, $data_size * 2);
2188 $offset += ($data_size * 2);
2189 break;
2190 default:
2191 echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: '.$data_size_type.'<br>';
2192 break 2;
2193 }
2194
2195 switch ($record_type) {
2196 case 0x00000011: // CreateDate
2197 case 0x00000012: // DateTimeOriginal
2198 $data = strtotime($data);
2199 break;
2200 case 0x0200001e: // ColorSpace
2201 switch ($data) {
2202 case 1:
2203 $data = 'sRGB';
2204 break;
2205 case 2:
2206 $data = 'Adobe RGB';
2207 break;
2208 }
2209 break;
2210 case 0x02000023: // PictureControlData
2211 $PictureControlAdjust = array(0=>'default', 1=>'quick', 2=>'full');
2212 $FilterEffect = array(0x80=>'off', 0x81=>'yellow', 0x82=>'orange', 0x83=>'red', 0x84=>'green', 0xff=>'n/a');
2213 $ToningEffect = array(0x80=>'b&w', 0x81=>'sepia', 0x82=>'cyanotype', 0x83=>'red', 0x84=>'yellow', 0x85=>'green', 0x86=>'blue-green', 0x87=>'blue', 0x88=>'purple-blue', 0x89=>'red-purple', 0xff=>'n/a');
2214 $data = array(
2215 'PictureControlVersion' => substr($data, 0, 4),
2216 'PictureControlName' => rtrim(substr($data, 4, 20), "\x00"),
2217 'PictureControlBase' => rtrim(substr($data, 24, 20), "\x00"),
2218 //'?' => substr($data, 44, 4),
2219 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))],
2220 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)),
2221 'Sharpness' => ord(substr($data, 50, 1)),
2222 'Contrast' => ord(substr($data, 51, 1)),
2223 'Brightness' => ord(substr($data, 52, 1)),
2224 'Saturation' => ord(substr($data, 53, 1)),
2225 'HueAdjustment' => ord(substr($data, 54, 1)),
2226 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))],
2227 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))],
2228 'ToningSaturation' => ord(substr($data, 57, 1)),
2229 );
2230 break;
2231 case 0x02000024: // WorldTime
2232 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
2233 // timezone is stored as offset from GMT in minutes
2234 $timezone = getid3_lib::BigEndian2Int(substr($data, 0, 2));
2235 if ($timezone & 0x8000) {
2236 $timezone = 0 - (0x10000 - $timezone);
2237 }
2238 $timezone /= 60;
2239
2240 $dst = (bool) getid3_lib::BigEndian2Int(substr($data, 2, 1));
2241 switch (getid3_lib::BigEndian2Int(substr($data, 3, 1))) {
2242 case 2:
2243 $datedisplayformat = 'D/M/Y'; break;
2244 case 1:
2245 $datedisplayformat = 'M/D/Y'; break;
2246 case 0:
2247 default:
2248 $datedisplayformat = 'Y/M/D'; break;
2249 }
2250
2251 $data = array('timezone'=>floatval($timezone), 'dst'=>$dst, 'display'=>$datedisplayformat);
2252 break;
2253 case 0x02000083: // LensType
2254 $data = array(
2255 //'_' => $data,
2256 'mf' => (bool) ($data & 0x01),
2257 'd' => (bool) ($data & 0x02),
2258 'g' => (bool) ($data & 0x04),
2259 'vr' => (bool) ($data & 0x08),
2260 );
2261 break;
2262 }
2263 $tag_name = (isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x'.str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT));
2264 $parsed[$tag_name] = $data;
2265 }
2266 return $parsed;
2267 }
2268
2269
2270 public function CopyToAppropriateCommentsSection($keyname, $data, $boxname='') {
2271 static $handyatomtranslatorarray = array();
2272 if (empty($handyatomtranslatorarray)) {
2273 // http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt
2274 // http://www.geocities.com/xhelmboyx/quicktime/formats/mp4-layout.txt
2275 // http://atomicparsley.sourceforge.net/mpeg-4files.html
2276 // https://code.google.com/p/mp4v2/wiki/iTunesMetadata
2277 $handyatomtranslatorarray["\xA9".'alb'] = 'album'; // iTunes 4.0
2278 $handyatomtranslatorarray["\xA9".'ART'] = 'artist';
2279 $handyatomtranslatorarray["\xA9".'art'] = 'artist'; // iTunes 4.0
2280 $handyatomtranslatorarray["\xA9".'aut'] = 'author';
2281 $handyatomtranslatorarray["\xA9".'cmt'] = 'comment'; // iTunes 4.0
2282 $handyatomtranslatorarray["\xA9".'com'] = 'comment';
2283 $handyatomtranslatorarray["\xA9".'cpy'] = 'copyright';
2284 $handyatomtranslatorarray["\xA9".'day'] = 'creation_date'; // iTunes 4.0
2285 $handyatomtranslatorarray["\xA9".'dir'] = 'director';
2286 $handyatomtranslatorarray["\xA9".'ed1'] = 'edit1';
2287 $handyatomtranslatorarray["\xA9".'ed2'] = 'edit2';
2288 $handyatomtranslatorarray["\xA9".'ed3'] = 'edit3';
2289 $handyatomtranslatorarray["\xA9".'ed4'] = 'edit4';
2290 $handyatomtranslatorarray["\xA9".'ed5'] = 'edit5';
2291 $handyatomtranslatorarray["\xA9".'ed6'] = 'edit6';
2292 $handyatomtranslatorarray["\xA9".'ed7'] = 'edit7';
2293 $handyatomtranslatorarray["\xA9".'ed8'] = 'edit8';
2294 $handyatomtranslatorarray["\xA9".'ed9'] = 'edit9';
2295 $handyatomtranslatorarray["\xA9".'enc'] = 'encoded_by';
2296 $handyatomtranslatorarray["\xA9".'fmt'] = 'format';
2297 $handyatomtranslatorarray["\xA9".'gen'] = 'genre'; // iTunes 4.0
2298 $handyatomtranslatorarray["\xA9".'grp'] = 'grouping'; // iTunes 4.2
2299 $handyatomtranslatorarray["\xA9".'hst'] = 'host_computer';
2300 $handyatomtranslatorarray["\xA9".'inf'] = 'information';
2301 $handyatomtranslatorarray["\xA9".'lyr'] = 'lyrics'; // iTunes 5.0
2302 $handyatomtranslatorarray["\xA9".'mak'] = 'make';
2303 $handyatomtranslatorarray["\xA9".'mod'] = 'model';
2304 $handyatomtranslatorarray["\xA9".'nam'] = 'title'; // iTunes 4.0
2305 $handyatomtranslatorarray["\xA9".'ope'] = 'composer';
2306 $handyatomtranslatorarray["\xA9".'prd'] = 'producer';
2307 $handyatomtranslatorarray["\xA9".'PRD'] = 'product';
2308 $handyatomtranslatorarray["\xA9".'prf'] = 'performers';
2309 $handyatomtranslatorarray["\xA9".'req'] = 'system_requirements';
2310 $handyatomtranslatorarray["\xA9".'src'] = 'source_credit';
2311 $handyatomtranslatorarray["\xA9".'swr'] = 'software';
2312 $handyatomtranslatorarray["\xA9".'too'] = 'encoding_tool'; // iTunes 4.0
2313 $handyatomtranslatorarray["\xA9".'trk'] = 'track';
2314 $handyatomtranslatorarray["\xA9".'url'] = 'url';
2315 $handyatomtranslatorarray["\xA9".'wrn'] = 'warning';
2316 $handyatomtranslatorarray["\xA9".'wrt'] = 'composer';
2317 $handyatomtranslatorarray['aART'] = 'album_artist';
2318 $handyatomtranslatorarray['apID'] = 'purchase_account';
2319 $handyatomtranslatorarray['catg'] = 'category'; // iTunes 4.9
2320 $handyatomtranslatorarray['covr'] = 'picture'; // iTunes 4.0
2321 $handyatomtranslatorarray['cpil'] = 'compilation'; // iTunes 4.0
2322 $handyatomtranslatorarray['cprt'] = 'copyright'; // iTunes 4.0?
2323 $handyatomtranslatorarray['desc'] = 'description'; // iTunes 5.0
2324 $handyatomtranslatorarray['disk'] = 'disc_number'; // iTunes 4.0
2325 $handyatomtranslatorarray['egid'] = 'episode_guid'; // iTunes 4.9
2326 $handyatomtranslatorarray['gnre'] = 'genre'; // iTunes 4.0
2327 $handyatomtranslatorarray['hdvd'] = 'hd_video'; // iTunes 4.0
2328 $handyatomtranslatorarray['ldes'] = 'description_long'; //
2329 $handyatomtranslatorarray['keyw'] = 'keyword'; // iTunes 4.9
2330 $handyatomtranslatorarray['pcst'] = 'podcast'; // iTunes 4.9
2331 $handyatomtranslatorarray['pgap'] = 'gapless_playback'; // iTunes 7.0
2332 $handyatomtranslatorarray['purd'] = 'purchase_date'; // iTunes 6.0.2
2333 $handyatomtranslatorarray['purl'] = 'podcast_url'; // iTunes 4.9
2334 $handyatomtranslatorarray['rtng'] = 'rating'; // iTunes 4.0
2335 $handyatomtranslatorarray['soaa'] = 'sort_album_artist'; //
2336 $handyatomtranslatorarray['soal'] = 'sort_album'; //
2337 $handyatomtranslatorarray['soar'] = 'sort_artist'; //
2338 $handyatomtranslatorarray['soco'] = 'sort_composer'; //
2339 $handyatomtranslatorarray['sonm'] = 'sort_title'; //
2340 $handyatomtranslatorarray['sosn'] = 'sort_show'; //
2341 $handyatomtranslatorarray['stik'] = 'stik'; // iTunes 4.9
2342 $handyatomtranslatorarray['tmpo'] = 'bpm'; // iTunes 4.0
2343 $handyatomtranslatorarray['trkn'] = 'track_number'; // iTunes 4.0
2344 $handyatomtranslatorarray['tven'] = 'tv_episode_id'; //
2345 $handyatomtranslatorarray['tves'] = 'tv_episode'; // iTunes 6.0
2346 $handyatomtranslatorarray['tvnn'] = 'tv_network_name'; // iTunes 6.0
2347 $handyatomtranslatorarray['tvsh'] = 'tv_show_name'; // iTunes 6.0
2348 $handyatomtranslatorarray['tvsn'] = 'tv_season'; // iTunes 6.0
2349
2350 // boxnames:
2351 /*
2352 $handyatomtranslatorarray['iTunSMPB'] = 'iTunSMPB';
2353 $handyatomtranslatorarray['iTunNORM'] = 'iTunNORM';
2354 $handyatomtranslatorarray['Encoding Params'] = 'Encoding Params';
2355 $handyatomtranslatorarray['replaygain_track_gain'] = 'replaygain_track_gain';
2356 $handyatomtranslatorarray['replaygain_track_peak'] = 'replaygain_track_peak';
2357 $handyatomtranslatorarray['replaygain_track_minmax'] = 'replaygain_track_minmax';
2358 $handyatomtranslatorarray['MusicIP PUID'] = 'MusicIP PUID';
2359 $handyatomtranslatorarray['MusicBrainz Artist Id'] = 'MusicBrainz Artist Id';
2360 $handyatomtranslatorarray['MusicBrainz Album Id'] = 'MusicBrainz Album Id';
2361 $handyatomtranslatorarray['MusicBrainz Album Artist Id'] = 'MusicBrainz Album Artist Id';
2362 $handyatomtranslatorarray['MusicBrainz Track Id'] = 'MusicBrainz Track Id';
2363 $handyatomtranslatorarray['MusicBrainz Disc Id'] = 'MusicBrainz Disc Id';
2364
2365 // http://age.hobba.nl/audio/tag_frame_reference.html
2366 $handyatomtranslatorarray['PLAY_COUNTER'] = 'play_counter'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2367 $handyatomtranslatorarray['MEDIATYPE'] = 'mediatype'; // Foobar2000 - http://www.getid3.org/phpBB3/viewtopic.php?t=1355
2368 */
2369 }
2370 $info = &$this->getid3->info;
2371 $comment_key = '';
2372 if ($boxname && ($boxname != $keyname)) {
2373 $comment_key = (isset($handyatomtranslatorarray[$boxname]) ? $handyatomtranslatorarray[$boxname] : $boxname);
2374 } elseif (isset($handyatomtranslatorarray[$keyname])) {
2375 $comment_key = $handyatomtranslatorarray[$keyname];
2376 }
2377 if ($comment_key) {
2378 if ($comment_key == 'picture') {
2379 if (!is_array($data)) {
2380 $image_mime = '';
2381 if (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $data)) {
2382 $image_mime = 'image/png';
2383 } elseif (preg_match('#^\xFF\xD8\xFF#', $data)) {
2384 $image_mime = 'image/jpeg';
2385 } elseif (preg_match('#^GIF#', $data)) {
2386 $image_mime = 'image/gif';
2387 } elseif (preg_match('#^BM#', $data)) {
2388 $image_mime = 'image/bmp';
2389 }
2390 $data = array('data'=>$data, 'image_mime'=>$image_mime);
2391 }
2392 }
2393 $gooddata = array($data);
2394 if ($comment_key == 'genre') {
2395 // some other taggers separate multiple genres with semicolon, e.g. "Heavy Metal;Thrash Metal;Metal"
2396 $gooddata = explode(';', $data);
2397 }
2398 foreach ($gooddata as $data) {
2399 $info['quicktime']['comments'][$comment_key][] = $data;
2400 }
2401 }
2402 return true;
2403 }
2404
2405 public function NoNullString($nullterminatedstring) {
2406 // remove the single null terminator on null terminated strings
2407 if (substr($nullterminatedstring, strlen($nullterminatedstring) - 1, 1) === "\x00") {
2408 return substr($nullterminatedstring, 0, strlen($nullterminatedstring) - 1);
2409 }
2410 return $nullterminatedstring;
2411 }
2412
2413 public function Pascal2String($pascalstring) {
2414 // Pascal strings have 1 unsigned byte at the beginning saying how many chars (1-255) are in the string
2415 return substr($pascalstring, 1);
2416 }
2417
2418
2419 /*
2420 // helper functions for m4b audiobook chapters
2421 // code by Steffen Hartmann 2015-Nov-08
2422 */
2423 public function search_tag_by_key($info, $tag, $history, &$result) {
2424 foreach ($info as $key => $value) {
2425 $key_history = $history.'/'.$key;
2426 if ($key === $tag) {
2427 $result[] = array($key_history, $info);
2428 } else {
2429 if (is_array($value)) {
2430 $this->search_tag_by_key($value, $tag, $key_history, $result);
2431 }
2432 }
2433 }
2434 }
2435
2436 public function search_tag_by_pair($info, $k, $v, $history, &$result) {
2437 foreach ($info as $key => $value) {
2438 $key_history = $history.'/'.$key;
2439 if (($key === $k) && ($value === $v)) {
2440 $result[] = array($key_history, $info);
2441 } else {
2442 if (is_array($value)) {
2443 $this->search_tag_by_pair($value, $k, $v, $key_history, $result);
2444 }
2445 }
2446 }
2447 }
2448
2449 public function quicktime_time_to_sample_table($info) {
2450 $res = array();
2451 $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
2452 foreach ($res as $value) {
2453 $stbl_res = array();
2454 $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
2455 if (count($stbl_res) > 0) {
2456 $stts_res = array();
2457 $this->search_tag_by_key($value[1], 'time_to_sample_table', $value[0], $stts_res);
2458 if (count($stts_res) > 0) {
2459 return $stts_res[0][1]['time_to_sample_table'];
2460 }
2461 }
2462 }
2463 return array();
2464 }
2465
2466 function quicktime_bookmark_time_scale($info) {
2467 $time_scale = '';
2468 $ts_prefix_len = 0;
2469 $res = array();
2470 $this->search_tag_by_pair($info['quicktime']['moov'], 'name', 'stbl', 'quicktime/moov', $res);
2471 foreach ($res as $value) {
2472 $stbl_res = array();
2473 $this->search_tag_by_pair($value[1], 'data_format', 'text', $value[0], $stbl_res);
2474 if (count($stbl_res) > 0) {
2475 $ts_res = array();
2476 $this->search_tag_by_key($info['quicktime']['moov'], 'time_scale', 'quicktime/moov', $ts_res);
2477 foreach ($ts_res as $value) {
2478 $prefix = substr($value[0], 0, -12);
2479 if ((substr($stbl_res[0][0], 0, strlen($prefix)) === $prefix) && ($ts_prefix_len < strlen($prefix))) {
2480 $time_scale = $value[1]['time_scale'];
2481 $ts_prefix_len = strlen($prefix);
2482 }
2483 }
2484 }
2485 }
2486 return $time_scale;
2487 }
2488 /*
2489 // END helper functions for m4b audiobook chapters
2490 */
2491
2492
2493 }