1
|
|
|
<?php |
2
|
|
|
// +----------------------------------------------------------------------+ |
3
|
|
|
// | PHP version 5 | |
4
|
|
|
// +----------------------------------------------------------------------+ |
5
|
|
|
// | Copyright (c) 2002-2006 James Heinrich, Allan Hansen | |
6
|
|
|
// +----------------------------------------------------------------------+ |
7
|
|
|
// | This source file is subject to version 2 of the GPL license, | |
8
|
|
|
// | that is bundled with this package in the file license.txt and is | |
9
|
|
|
// | available through the world-wide-web at the following url: | |
10
|
|
|
// | http://www.gnu.org/copyleft/gpl.html | |
11
|
|
|
// +----------------------------------------------------------------------+ |
12
|
|
|
// | getID3() - http://getid3.sourceforge.net or http://www.getid3.org | |
13
|
|
|
// +----------------------------------------------------------------------+ |
14
|
|
|
// | Authors: James Heinrich <info�getid3*org> | |
15
|
|
|
// | Allan Hansen <ah�artemis*dk> | |
16
|
|
|
// +----------------------------------------------------------------------+ |
17
|
|
|
// | module.audio-video.quicktime.php | |
18
|
|
|
// | Module for analyzing Quicktime, MP3-in-MP4 and Apple Lossless files. | |
19
|
|
|
// | dependencies: module.audio.mp3.php | |
20
|
|
|
// | zlib support in PHP (optional) | |
21
|
|
|
// +----------------------------------------------------------------------+ |
22
|
|
|
// |
23
|
|
|
// $Id: module.audio-video.quicktime.php,v 1.7 2006/11/02 16:03:28 ah Exp $ |
24
|
|
|
|
25
|
|
|
class getid3_quicktime extends getid3_handler |
26
|
|
|
{ |
27
|
|
|
|
28
|
|
|
public function Analyze() |
29
|
|
|
{ |
30
|
|
|
$getid3 = $this->getid3; |
31
|
|
|
|
32
|
|
|
$info = &$getid3->info; |
33
|
|
|
|
34
|
|
|
$getid3->include_module('audio.mp3'); |
35
|
|
|
|
36
|
|
|
$info['quicktime'] = []; |
37
|
|
|
$info_quicktime = &$info['quicktime']; |
38
|
|
|
|
39
|
|
|
$info['fileformat'] = 'quicktime'; |
40
|
|
|
$info_quicktime['hinting'] = false; |
41
|
|
|
|
42
|
|
|
fseek($getid3->fp, $info['avdataoffset'], SEEK_SET); |
43
|
|
|
|
44
|
|
|
$offset = $atom_counter = 0; |
45
|
|
|
|
46
|
|
|
while ($offset < $info['avdataend']) { |
47
|
|
|
fseek($getid3->fp, $offset, SEEK_SET); |
48
|
|
|
$atom_header = fread($getid3->fp, 8); |
49
|
|
|
|
50
|
|
|
$atom_size = getid3_lib::BigEndian2Int(substr($atom_header, 0, 4)); |
51
|
|
|
$atom_name = substr($atom_header, 4, 4); |
52
|
|
|
|
53
|
|
|
$info_quicktime[$atom_name]['name'] = $atom_name; |
54
|
|
|
$info_quicktime[$atom_name]['size'] = $atom_size; |
55
|
|
|
$info_quicktime[$atom_name]['offset'] = $offset; |
56
|
|
|
|
57
|
|
|
if (($offset + $atom_size) > $info['avdataend']) { |
58
|
|
|
throw new getid3_exception('Atom at offset ' . $offset . ' claims to go beyond end-of-file (length: ' . $atom_size . ' bytes)'); |
59
|
|
|
} |
60
|
|
|
|
61
|
|
|
if (0 == $atom_size) { |
62
|
|
|
// Furthermore, for historical reasons the list of atoms is optionally |
63
|
|
|
// terminated by a 32-bit integer set to 0. If you are writing a program |
64
|
|
|
// to read user data atoms, you should allow for the terminating 0. |
65
|
|
|
break; |
66
|
|
|
} |
67
|
|
|
|
68
|
|
|
switch ($atom_name) { |
69
|
|
|
case 'mdat': // Media DATa atom |
70
|
|
|
// 'mdat' contains the actual data for the audio/video |
71
|
|
|
if (($atom_size > 8) && (!isset($info['avdataend_tmp']) || ($info_quicktime[$atom_name]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { |
72
|
|
|
$info['avdataoffset'] = $info_quicktime[$atom_name]['offset'] + 8; |
73
|
|
|
$old_av_data_end = $info['avdataend']; |
74
|
|
|
$info['avdataend'] = $info_quicktime[$atom_name]['offset'] + $info_quicktime[$atom_name]['size']; |
75
|
|
|
|
76
|
|
|
//// MP3 |
77
|
|
|
|
78
|
|
|
if (!$getid3->include_module_optional('audio.mp3')) { |
|
|
|
|
79
|
|
|
$getid3->warning('MP3 skipped because mpeg module is missing.'); |
80
|
|
|
} else { |
81
|
|
|
// Clone getid3 - messing with offsets - better safe than sorry |
82
|
|
|
$clone = clone $getid3; |
83
|
|
|
|
84
|
|
|
if (getid3_mp3::MPEGaudioHeaderValid(getid3_mp3::MPEGaudioHeaderDecode(fread($clone->fp, 4)))) { |
85
|
|
|
$mp3 = new getid3_mp3($clone); |
86
|
|
|
$mp3->AnalyzeMPEGaudioInfo(); |
87
|
|
|
|
88
|
|
|
// Import from clone and destroy |
89
|
|
|
if (isset($clone->info['mpeg']['audio'])) { |
90
|
|
|
$info['mpeg']['audio'] = $clone->info['mpeg']['audio']; |
91
|
|
|
|
92
|
|
|
$info['audio']['dataformat'] = 'mp3'; |
93
|
|
|
$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'))); |
94
|
|
|
$info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; |
95
|
|
|
$info['audio']['channels'] = $info['mpeg']['audio']['channels']; |
96
|
|
|
$info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; |
97
|
|
|
$info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); |
98
|
|
|
$info['bitrate'] = $info['audio']['bitrate']; |
99
|
|
|
|
100
|
|
|
$getid3->warning($clone->warnings()); |
101
|
|
|
unset($clone); |
102
|
|
|
} |
103
|
|
|
} |
104
|
|
|
} |
105
|
|
|
|
106
|
|
|
$info['avdataend'] = $old_av_data_end; |
107
|
|
|
unset($old_av_data_end); |
108
|
|
|
} |
109
|
|
|
break; |
110
|
|
|
|
111
|
|
|
case 'free': // FREE space atom |
112
|
|
|
case 'skip': // SKIP atom |
113
|
|
|
case 'wide': // 64-bit expansion placeholder atom |
114
|
|
|
// 'free', 'skip' and 'wide' are just padding, contains no useful data at all |
115
|
|
|
break; |
116
|
|
|
|
117
|
|
|
default: |
118
|
|
|
$atom_hierarchy = []; |
119
|
|
|
$info_quicktime[$atom_name] = $this->QuicktimeParseAtom($atom_name, $atom_size, fread($getid3->fp, $atom_size), $offset, $atom_hierarchy); |
120
|
|
|
break; |
121
|
|
|
} |
122
|
|
|
|
123
|
|
|
$offset += $atom_size; |
124
|
|
|
$atom_counter++; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
if (!empty($info['avdataend_tmp'])) { |
128
|
|
|
// this value is assigned to a temp value and then erased because |
129
|
|
|
// otherwise any atoms beyond the 'mdat' atom would not get parsed |
130
|
|
|
$info['avdataend'] = $info['avdataend_tmp']; |
131
|
|
|
unset($info['avdataend_tmp']); |
132
|
|
|
} |
133
|
|
|
|
134
|
|
|
if (!isset($info['bitrate']) && isset($info['playtime_seconds'])) { |
135
|
|
|
$info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds']; |
136
|
|
|
} |
137
|
|
|
|
138
|
|
|
if (isset($info['bitrate']) && !isset($info['audio']['bitrate']) && !isset($info_quicktime['video'])) { |
139
|
|
|
$info['audio']['bitrate'] = $info['bitrate']; |
140
|
|
|
} |
141
|
|
|
|
142
|
|
|
if (('mp4' == @$info['audio']['dataformat']) && empty($info['video']['resolution_x'])) { |
143
|
|
|
$info['fileformat'] = 'mp4'; |
144
|
|
|
$info['mime_type'] = 'audio/mp4'; |
145
|
|
|
unset($info['video']['dataformat']); |
146
|
|
|
} |
147
|
|
|
|
148
|
|
|
if (!$getid3->option_extra_info) { |
149
|
|
|
unset($info_quicktime['moov']); |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
if (empty($info['audio']['dataformat']) && !empty($info_quicktime['audio'])) { |
153
|
|
|
$info['audio']['dataformat'] = 'quicktime'; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
if (empty($info['video']['dataformat']) && !empty($info_quicktime['video'])) { |
157
|
|
|
$info['video']['dataformat'] = 'quicktime'; |
158
|
|
|
} |
159
|
|
|
|
160
|
|
|
return true; |
161
|
|
|
} |
162
|
|
|
|
163
|
|
|
private function QuicktimeParseAtom($atom_name, $atom_size, $atom_data, $base_offset, &$atom_hierarchy) |
164
|
|
|
{ |
165
|
|
|
// http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm |
166
|
|
|
|
167
|
|
|
$getid3 = $this->getid3; |
168
|
|
|
|
169
|
|
|
$info = &$getid3->info; |
170
|
|
|
$info_quicktime = &$info['quicktime']; |
171
|
|
|
|
172
|
|
|
array_push($atom_hierarchy, $atom_name); |
173
|
|
|
$atom_structure['hierarchy'] = implode(' ', $atom_hierarchy); |
|
|
|
|
174
|
|
|
$atom_structure['name'] = $atom_name; |
175
|
|
|
$atom_structure['size'] = $atom_size; |
176
|
|
|
$atom_structure['offset'] = $base_offset; |
177
|
|
|
|
178
|
|
|
switch ($atom_name) { |
179
|
|
|
case 'moov': // MOVie container atom |
180
|
|
|
case 'trak': // TRAcK container atom |
181
|
|
|
case 'clip': // CLIPping container atom |
182
|
|
|
case 'matt': // track MATTe container atom |
183
|
|
|
case 'edts': // EDiTS container atom |
184
|
|
|
case 'tref': // Track REFerence container atom |
185
|
|
|
case 'mdia': // MeDIA container atom |
186
|
|
|
case 'minf': // Media INFormation container atom |
187
|
|
|
case 'dinf': // Data INFormation container atom |
188
|
|
|
case 'udta': // User DaTA container atom |
189
|
|
|
case 'stbl': // Sample TaBLe container atom |
190
|
|
|
case 'cmov': // Compressed MOVie container atom |
191
|
|
|
case 'rmra': // Reference Movie Record Atom |
192
|
|
|
case 'rmda': // Reference Movie Descriptor Atom |
193
|
|
|
case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) |
194
|
|
|
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $base_offset + 8, $atom_hierarchy); |
195
|
|
|
break; |
196
|
|
|
|
197
|
|
|
case '�cpy': |
198
|
|
|
case '�day': |
199
|
|
|
case '�dir': |
200
|
|
|
case '�ed1': |
201
|
|
|
case '�ed2': |
202
|
|
|
case '�ed3': |
203
|
|
|
case '�ed4': |
204
|
|
|
case '�ed5': |
205
|
|
|
case '�ed6': |
206
|
|
|
case '�ed7': |
207
|
|
|
case '�ed8': |
208
|
|
|
case '�ed9': |
209
|
|
|
case '�fmt': |
210
|
|
|
case '�inf': |
211
|
|
|
case '�prd': |
212
|
|
|
case '�prf': |
213
|
|
|
case '�req': |
214
|
|
|
case '�src': |
215
|
|
|
case '�wrt': |
216
|
|
|
case '�nam': |
217
|
|
|
case '�cmt': |
218
|
|
|
case '�wrn': |
219
|
|
|
case '�hst': |
220
|
|
|
case '�mak': |
221
|
|
|
case '�mod': |
222
|
|
|
case '�PRD': |
223
|
|
|
case '�swr': |
224
|
|
|
case '�aut': |
225
|
|
|
case '�ART': |
226
|
|
|
case '�trk': |
227
|
|
|
case '�alb': |
228
|
|
|
case '�com': |
229
|
|
|
case '�gen': |
230
|
|
|
case '�ope': |
231
|
|
|
case '�url': |
232
|
|
|
case '�enc': |
233
|
|
|
$atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); |
234
|
|
|
$atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); |
235
|
|
|
$atom_structure['data'] = substr($atom_data, 4); |
236
|
|
|
|
237
|
|
|
$atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); |
238
|
|
|
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { |
239
|
|
|
$info['comments']['language'][] = $atom_structure['language']; |
240
|
|
|
} |
241
|
|
|
$this->CopyToAppropriateCommentsSection($atom_name, $atom_structure['data']); |
242
|
|
|
break; |
243
|
|
|
|
244
|
|
|
case 'play': // auto-PLAY atom |
245
|
|
|
$atom_structure['autoplay'] = (bool)getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
246
|
|
|
|
247
|
|
|
$info_quicktime['autoplay'] = $atom_structure['autoplay']; |
248
|
|
|
break; |
249
|
|
|
|
250
|
|
|
case 'WLOC': // Window LOCation atom |
251
|
|
|
$atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); |
252
|
|
|
$atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); |
253
|
|
|
break; |
254
|
|
|
|
255
|
|
|
case 'LOOP': // LOOPing atom |
256
|
|
|
case 'SelO': // play SELection Only atom |
257
|
|
|
case 'AllF': // play ALL Frames atom |
258
|
|
|
$atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data); |
259
|
|
|
break; |
260
|
|
|
|
261
|
|
|
case 'name': // |
262
|
|
|
case 'MCPS': // Media Cleaner PRo |
263
|
|
|
case '@PRM': // adobe PReMiere version |
264
|
|
|
case '@PRQ': // adobe PRemiere Quicktime version |
265
|
|
|
$atom_structure['data'] = $atom_data; |
266
|
|
|
break; |
267
|
|
|
|
268
|
|
|
case 'cmvd': // Compressed MooV Data atom |
269
|
|
|
// Code by ubergeek�ubergeek*tv based on information from |
270
|
|
|
// http://developer.apple.com/quicktime/icefloe/dispatch012.html |
271
|
|
|
$atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
272
|
|
|
|
273
|
|
|
$compressed_file_data = substr($atom_data, 4); |
274
|
|
|
if (!function_exists('gzuncompress')) { |
275
|
|
|
$getid3->warning('PHP does not have zlib support - cannot decompress MOV atom at offset ' . $atom_structure['offset']); |
276
|
|
|
} elseif ($uncompressed_header = @gzuncompress($compressed_file_data)) { |
277
|
|
|
$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($uncompressed_header, 0, $atom_hierarchy); |
278
|
|
|
} else { |
279
|
|
|
$getid3->warning('Error decompressing compressed MOV atom at offset ' . $atom_structure['offset']); |
280
|
|
|
} |
281
|
|
|
break; |
282
|
|
|
|
283
|
|
|
case 'dcom': // Data COMpression atom |
284
|
|
|
$atom_structure['compression_id'] = $atom_data; |
285
|
|
|
$atom_structure['compression_text'] = getid3_quicktime::QuicktimeDCOMLookup($atom_data); |
286
|
|
|
break; |
287
|
|
|
|
288
|
|
|
case 'rdrf': // Reference movie Data ReFerence atom |
289
|
|
|
getid3_lib::ReadSequence( |
290
|
|
|
'BigEndian2Int', |
291
|
|
|
$atom_structure, |
292
|
|
|
$atom_data, |
293
|
|
|
0, |
294
|
|
|
[ |
295
|
|
|
'version' => 1, |
296
|
|
|
'flags_raw' => 3, |
297
|
|
|
'reference_type_name' => -4, |
298
|
|
|
'reference_length' => 4, |
299
|
|
|
] |
300
|
|
|
); |
301
|
|
|
|
302
|
|
|
$atom_structure['flags']['internal_data'] = (bool)($atom_structure['flags_raw'] & 0x000001); |
303
|
|
|
|
304
|
|
|
switch ($atom_structure['reference_type_name']) { |
305
|
|
|
case 'url ': |
306
|
|
|
$atom_structure['url'] = $this->NoNullString(substr($atom_data, 12)); |
307
|
|
|
break; |
308
|
|
|
|
309
|
|
|
case 'alis': |
310
|
|
|
$atom_structure['file_alias'] = substr($atom_data, 12); |
311
|
|
|
break; |
312
|
|
|
|
313
|
|
|
case 'rsrc': |
314
|
|
|
$atom_structure['resource_alias'] = substr($atom_data, 12); |
315
|
|
|
break; |
316
|
|
|
|
317
|
|
|
default: |
318
|
|
|
$atom_structure['data'] = substr($atom_data, 12); |
319
|
|
|
break; |
320
|
|
|
} |
321
|
|
|
break; |
322
|
|
|
|
323
|
|
|
case 'rmqu': // Reference Movie QUality atom |
324
|
|
|
$atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data); |
325
|
|
|
break; |
326
|
|
|
|
327
|
|
|
case 'rmcs': // Reference Movie Cpu Speed atom |
328
|
|
|
getid3_lib::ReadSequence( |
329
|
|
|
'BigEndian2Int', |
330
|
|
|
$atom_structure, |
331
|
|
|
$atom_data, |
332
|
|
|
0, |
333
|
|
|
[ |
334
|
|
|
'version' => 1, |
335
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
336
|
|
|
'cpu_speed_rating' => 2 |
337
|
|
|
] |
338
|
|
|
); |
339
|
|
|
break; |
340
|
|
|
|
341
|
|
|
case 'rmvc': // Reference Movie Version Check atom |
342
|
|
|
getid3_lib::ReadSequence( |
343
|
|
|
'BigEndian2Int', |
344
|
|
|
$atom_structure, |
345
|
|
|
$atom_data, |
346
|
|
|
0, |
347
|
|
|
[ |
348
|
|
|
'version' => 1, |
349
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
350
|
|
|
'gestalt_selector' => -4, |
351
|
|
|
'gestalt_value_mask' => 4, |
352
|
|
|
'gestalt_value' => 4, |
353
|
|
|
'gestalt_check_type' => 2 |
354
|
|
|
] |
355
|
|
|
); |
356
|
|
|
break; |
357
|
|
|
|
358
|
|
|
case 'rmcd': // Reference Movie Component check atom |
359
|
|
|
getid3_lib::ReadSequence( |
360
|
|
|
'BigEndian2Int', |
361
|
|
|
$atom_structure, |
362
|
|
|
$atom_data, |
363
|
|
|
0, |
364
|
|
|
[ |
365
|
|
|
'version' => 1, |
366
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
367
|
|
|
'component_type' => -4, |
368
|
|
|
'component_subtype' => -4, |
369
|
|
|
'component_manufacturer' => -4, |
370
|
|
|
'component_flags_raw' => 4, |
371
|
|
|
'component_flags_mask' => 4, |
372
|
|
|
'component_min_version' => 4 |
373
|
|
|
] |
374
|
|
|
); |
375
|
|
|
break; |
376
|
|
|
|
377
|
|
|
case 'rmdr': // Reference Movie Data Rate atom |
378
|
|
|
getid3_lib::ReadSequence( |
379
|
|
|
'BigEndian2Int', |
380
|
|
|
$atom_structure, |
381
|
|
|
$atom_data, |
382
|
|
|
0, |
383
|
|
|
[ |
384
|
|
|
'version' => 1, |
385
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
386
|
|
|
'data_rate' => 4 |
387
|
|
|
] |
388
|
|
|
); |
389
|
|
|
|
390
|
|
|
$atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10; |
391
|
|
|
break; |
392
|
|
|
|
393
|
|
|
case 'rmla': // Reference Movie Language Atom |
394
|
|
|
getid3_lib::ReadSequence( |
395
|
|
|
'BigEndian2Int', |
396
|
|
|
$atom_structure, |
397
|
|
|
$atom_data, |
398
|
|
|
0, |
399
|
|
|
[ |
400
|
|
|
'version' => 1, |
401
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
402
|
|
|
'language_id' => 2 |
403
|
|
|
] |
404
|
|
|
); |
405
|
|
|
|
406
|
|
|
$atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); |
407
|
|
|
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { |
408
|
|
|
$info['comments']['language'][] = $atom_structure['language']; |
409
|
|
|
} |
410
|
|
|
break; |
411
|
|
|
|
412
|
|
|
case 'rmla': // Reference Movie Language Atom |
413
|
|
|
getid3_lib::ReadSequence( |
414
|
|
|
'BigEndian2Int', |
415
|
|
|
$atom_structure, |
416
|
|
|
$atom_data, |
417
|
|
|
0, |
418
|
|
|
[ |
419
|
|
|
'version' => 1, |
420
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
421
|
|
|
'track_id' => 2 |
422
|
|
|
] |
423
|
|
|
); |
424
|
|
|
break; |
425
|
|
|
|
426
|
|
|
case 'ptv ': // Print To Video - defines a movie's full screen mode |
427
|
|
|
// http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm |
428
|
|
|
getid3_lib::ReadSequence( |
429
|
|
|
'BigEndian2Int', |
430
|
|
|
$atom_structure, |
431
|
|
|
$atom_data, |
432
|
|
|
0, |
433
|
|
|
[ |
434
|
|
|
'display_size_raw' => 2, |
435
|
|
|
'reserved_1' => 2, // hardcoded: 0x0000 |
436
|
|
|
'reserved_2' => 2, // hardcoded: 0x0000 |
437
|
|
|
'slide_show_flag' => 1, |
438
|
|
|
'play_on_open_flag' => 1 |
439
|
|
|
] |
440
|
|
|
); |
441
|
|
|
|
442
|
|
|
$atom_structure['flags']['play_on_open'] = (bool)$atom_structure['play_on_open_flag']; |
443
|
|
|
$atom_structure['flags']['slide_show'] = (bool)$atom_structure['slide_show_flag']; |
444
|
|
|
|
445
|
|
|
$ptv_lookup[0] = 'normal'; |
|
|
|
|
446
|
|
|
$ptv_lookup[1] = 'double'; |
447
|
|
|
$ptv_lookup[2] = 'half'; |
448
|
|
|
$ptv_lookup[3] = 'full'; |
449
|
|
|
$ptv_lookup[4] = 'current'; |
450
|
|
|
if (isset($ptv_lookup[$atom_structure['display_size_raw']])) { |
451
|
|
|
$atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']]; |
452
|
|
|
} else { |
453
|
|
|
$getid3->warning('unknown "ptv " display constant (' . $atom_structure['display_size_raw'] . ')'); |
454
|
|
|
} |
455
|
|
|
break; |
456
|
|
|
|
457
|
|
|
case 'stsd': // Sample Table Sample Description atom |
458
|
|
|
getid3_lib::ReadSequence( |
459
|
|
|
'BigEndian2Int', |
460
|
|
|
$atom_structure, |
461
|
|
|
$atom_data, |
462
|
|
|
0, |
463
|
|
|
[ |
464
|
|
|
'version' => 1, |
465
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
466
|
|
|
'number_entries' => 4 |
467
|
|
|
] |
468
|
|
|
); |
469
|
|
|
$stsd_entries_data_offset = 8; |
470
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
471
|
|
|
getid3_lib::ReadSequence( |
472
|
|
|
'BigEndian2Int', |
473
|
|
|
$atom_structure['sample_description_table'][$i], |
474
|
|
|
$atom_data, |
475
|
|
|
$stsd_entries_data_offset, |
476
|
|
|
[ |
477
|
|
|
'size' => 4, |
478
|
|
|
'data_format' => -4, |
479
|
|
|
'reserved' => 6, |
480
|
|
|
'reference_index' => 2 |
481
|
|
|
] |
482
|
|
|
); |
483
|
|
|
|
484
|
|
|
$atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, 16 + $stsd_entries_data_offset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); |
485
|
|
|
$stsd_entries_data_offset += 16 + ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); |
486
|
|
|
|
487
|
|
|
getid3_lib::ReadSequence( |
488
|
|
|
'BigEndian2Int', |
489
|
|
|
$atom_structure['sample_description_table'][$i], |
490
|
|
|
$atom_structure['sample_description_table'][$i]['data'], |
491
|
|
|
0, |
492
|
|
|
[ |
493
|
|
|
'encoder_version' => 2, |
494
|
|
|
'encoder_revision' => 2, |
495
|
|
|
'encoder_vendor' => -4 |
496
|
|
|
] |
497
|
|
|
); |
498
|
|
|
|
499
|
|
|
switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) { |
500
|
|
|
case "\x00\x00\x00\x00": |
501
|
|
|
// audio atom |
502
|
|
|
getid3_lib::ReadSequence( |
503
|
|
|
'BigEndian2Int', |
504
|
|
|
$atom_structure['sample_description_table'][$i], |
505
|
|
|
$atom_structure['sample_description_table'][$i]['data'], |
506
|
|
|
8, |
507
|
|
|
[ |
508
|
|
|
'audio_channels' => 2, |
509
|
|
|
'audio_bit_depth' => 2, |
510
|
|
|
'audio_compression_id' => 2, |
511
|
|
|
'audio_packet_size' => 2 |
512
|
|
|
] |
513
|
|
|
); |
514
|
|
|
|
515
|
|
|
$atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_quicktime::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4)); |
516
|
|
|
|
517
|
|
|
switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
518
|
|
|
case 'mp4v': |
519
|
|
|
$info['fileformat'] = 'mp4'; |
520
|
|
|
throw new getid3_exception('This version of getID3() does not fully support MPEG-4 audio/video streams'); |
521
|
|
|
|
522
|
|
|
case 'qtvr': |
523
|
|
|
$info['video']['dataformat'] = 'quicktimevr'; |
524
|
|
|
break; |
525
|
|
|
|
526
|
|
|
case 'mp4a': |
527
|
|
|
default: |
528
|
|
|
$info_quicktime['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); |
529
|
|
|
$info_quicktime['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate']; |
530
|
|
|
$info_quicktime['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels']; |
531
|
|
|
$info_quicktime['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth']; |
532
|
|
|
$info['audio']['codec'] = $info_quicktime['audio']['codec']; |
533
|
|
|
$info['audio']['sample_rate'] = $info_quicktime['audio']['sample_rate']; |
534
|
|
|
$info['audio']['channels'] = $info_quicktime['audio']['channels']; |
535
|
|
|
$info['audio']['bits_per_sample'] = $info_quicktime['audio']['bit_depth']; |
536
|
|
|
switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
537
|
|
|
case 'raw ': // PCM |
538
|
|
|
case 'alac': // Apple Lossless Audio Codec |
539
|
|
|
$info['audio']['lossless'] = true; |
540
|
|
|
break; |
541
|
|
|
default: |
542
|
|
|
$info['audio']['lossless'] = false; |
543
|
|
|
break; |
544
|
|
|
} |
545
|
|
|
break; |
546
|
|
|
} |
547
|
|
|
break; |
548
|
|
|
|
549
|
|
|
default: |
550
|
|
|
switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
551
|
|
|
case 'mp4s': |
552
|
|
|
$info['fileformat'] = 'mp4'; |
553
|
|
|
break; |
554
|
|
|
|
555
|
|
|
default: |
556
|
|
|
// video atom |
557
|
|
|
getid3_lib::ReadSequence( |
558
|
|
|
'BigEndian2Int', |
559
|
|
|
$atom_structure['sample_description_table'][$i], |
560
|
|
|
$atom_structure['sample_description_table'][$i]['data'], |
561
|
|
|
8, |
562
|
|
|
[ |
563
|
|
|
'video_temporal_quality' => 4, |
564
|
|
|
'video_spatial_quality' => 4, |
565
|
|
|
'video_frame_width' => 2, |
566
|
|
|
'video_frame_height' => 2 |
567
|
|
|
] |
568
|
|
|
); |
569
|
|
|
$atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_quicktime::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4)); |
570
|
|
|
$atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_quicktime::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); |
571
|
|
|
getid3_lib::ReadSequence( |
572
|
|
|
'BigEndian2Int', |
573
|
|
|
$atom_structure['sample_description_table'][$i], |
574
|
|
|
$atom_structure['sample_description_table'][$i]['data'], |
575
|
|
|
28, |
576
|
|
|
[ |
577
|
|
|
'video_data_size' => 4, |
578
|
|
|
'video_frame_count' => 2, |
579
|
|
|
'video_encoder_name_len' => 1 |
580
|
|
|
] |
581
|
|
|
); |
582
|
|
|
$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']); |
583
|
|
|
$atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2)); |
584
|
|
|
$atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2)); |
585
|
|
|
|
586
|
|
|
$atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); |
587
|
|
|
$atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']); |
588
|
|
|
|
589
|
|
|
if ('invalid' != $atom_structure['sample_description_table'][$i]['video_pixel_color_name']) { |
590
|
|
|
$info_quicktime['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; |
591
|
|
|
$info_quicktime['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); |
592
|
|
|
$info_quicktime['video']['codec'] = $atom_structure['sample_description_table'][$i]['video_encoder_name']; |
593
|
|
|
$info_quicktime['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; |
594
|
|
|
$info_quicktime['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; |
595
|
|
|
|
596
|
|
|
$info['video']['codec'] = $info_quicktime['video']['codec']; |
597
|
|
|
$info['video']['bits_per_sample'] = $info_quicktime['video']['color_depth']; |
598
|
|
|
} |
599
|
|
|
$info['video']['lossless'] = false; |
600
|
|
|
$info['video']['pixel_aspect_ratio'] = (float)1; |
601
|
|
|
break; |
602
|
|
|
} |
603
|
|
|
break; |
604
|
|
|
} |
605
|
|
|
switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) { |
606
|
|
|
case 'mp4a': |
607
|
|
|
$info['audio']['dataformat'] = $info_quicktime['audio']['codec'] = 'mp4'; |
608
|
|
|
break; |
609
|
|
|
|
610
|
|
|
case '3ivx': |
611
|
|
|
case '3iv1': |
612
|
|
|
case '3iv2': |
613
|
|
|
$info['video']['dataformat'] = '3ivx'; |
614
|
|
|
break; |
615
|
|
|
|
616
|
|
|
case 'xvid': |
617
|
|
|
$info['video']['dataformat'] = 'xvid'; |
618
|
|
|
break; |
619
|
|
|
|
620
|
|
|
case 'mp4v': |
621
|
|
|
$info['video']['dataformat'] = 'mpeg4'; |
622
|
|
|
break; |
623
|
|
|
|
624
|
|
|
case 'divx': |
625
|
|
|
case 'div1': |
626
|
|
|
case 'div2': |
627
|
|
|
case 'div3': |
628
|
|
|
case 'div4': |
629
|
|
|
case 'div5': |
630
|
|
|
case 'div6': |
631
|
|
|
//$TDIVXileInfo['video']['dataformat'] = 'divx'; |
632
|
|
|
break; |
633
|
|
|
|
634
|
|
|
default: |
635
|
|
|
// do nothing |
636
|
|
|
break; |
637
|
|
|
} |
638
|
|
|
unset($atom_structure['sample_description_table'][$i]['data']); |
639
|
|
|
} |
640
|
|
|
break; |
641
|
|
|
|
642
|
|
|
case 'stts': // Sample Table Time-to-Sample atom |
643
|
|
|
getid3_lib::ReadSequence( |
644
|
|
|
'BigEndian2Int', |
645
|
|
|
$atom_structure, |
646
|
|
|
$atom_data, |
647
|
|
|
0, |
648
|
|
|
[ |
649
|
|
|
'version' => 1, |
650
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
651
|
|
|
'number_entries' => 4 |
652
|
|
|
] |
653
|
|
|
); |
654
|
|
|
|
655
|
|
|
$stts_entries_data_offset = 8; |
656
|
|
|
$frame_rate_calculator_array = []; |
|
|
|
|
657
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
658
|
|
|
$atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $stts_entries_data_offset, 4)); |
659
|
|
|
$stts_entries_data_offset += 4; |
660
|
|
|
|
661
|
|
|
$atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $stts_entries_data_offset, 4)); |
662
|
|
|
$stts_entries_data_offset += 4; |
663
|
|
|
|
664
|
|
|
if (!empty($info_quicktime['time_scale']) && (@$atoms_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) { |
|
|
|
|
665
|
|
|
$stts_new_framerate = $info_quicktime['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration']; |
666
|
|
|
if ($stts_new_framerate <= 60) { |
667
|
|
|
// some atoms have durations of "1" giving a very large framerate, which probably is not right |
668
|
|
|
$info['video']['frame_rate'] = max(@$info['video']['frame_rate'], $stts_new_framerate); |
669
|
|
|
} |
670
|
|
|
} |
671
|
|
|
//@$frame_rate_calculator_array[($info_quicktime['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; |
672
|
|
|
} |
673
|
|
|
/* |
674
|
|
|
$stts_frames_total = 0; |
675
|
|
|
$stts_seconds_total = 0; |
676
|
|
|
foreach ($frame_rate_calculator_array as $frames_per_second => $frame_count) { |
677
|
|
|
if (($frames_per_second > 60) || ($frames_per_second < 1)) { |
678
|
|
|
// not video FPS information, probably audio information |
679
|
|
|
$stts_frames_total = 0; |
680
|
|
|
$stts_seconds_total = 0; |
681
|
|
|
break; |
682
|
|
|
} |
683
|
|
|
$stts_frames_total += $frame_count; |
684
|
|
|
$stts_seconds_total += $frame_count / $frames_per_second; |
685
|
|
|
} |
686
|
|
|
if (($stts_frames_total > 0) && ($stts_seconds_total > 0)) { |
687
|
|
|
if (($stts_frames_total / $stts_seconds_total) > @$info['video']['frame_rate']) { |
688
|
|
|
$info['video']['frame_rate'] = $stts_frames_total / $stts_seconds_total; |
689
|
|
|
} |
690
|
|
|
} |
691
|
|
|
*/ |
692
|
|
|
break; |
693
|
|
|
|
694
|
|
|
case 'stss': // Sample Table Sync Sample (key frames) atom |
695
|
|
|
/* |
696
|
|
|
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
697
|
|
|
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
698
|
|
|
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
699
|
|
|
$stss_entries_data_offset = 8; |
700
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
701
|
|
|
$atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stss_entries_data_offset, 4)); |
702
|
|
|
$stss_entries_data_offset += 4; |
703
|
|
|
} |
704
|
|
|
*/ break; |
705
|
|
|
|
706
|
|
|
case 'stsc': // Sample Table Sample-to-Chunk atom |
707
|
|
|
/* |
708
|
|
|
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
709
|
|
|
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
710
|
|
|
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
711
|
|
|
$stsc_entries_data_offset = 8; |
712
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
713
|
|
|
$atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsc_entries_data_offset, 4)); |
714
|
|
|
$stsc_entries_data_offset += 4; |
715
|
|
|
$atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsc_entries_data_offset, 4)); |
716
|
|
|
$stsc_entries_data_offset += 4; |
717
|
|
|
$atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsc_entries_data_offset, 4)); |
718
|
|
|
$stsc_entries_data_offset += 4; |
719
|
|
|
} |
720
|
|
|
*/ break; |
721
|
|
|
|
722
|
|
|
case 'stsz': // Sample Table SiZe atom |
723
|
|
|
/* |
724
|
|
|
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
725
|
|
|
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
726
|
|
|
$atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
727
|
|
|
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
728
|
|
|
$stsz_entries_data_offset = 12; |
729
|
|
|
if ($atom_structure['sample_size'] == 0) { |
730
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
731
|
|
|
$atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stsz_entries_data_offset, 4)); |
732
|
|
|
$stsz_entries_data_offset += 4; |
733
|
|
|
} |
734
|
|
|
} |
735
|
|
|
*/ break; |
736
|
|
|
|
737
|
|
|
case 'stco': // Sample Table Chunk Offset atom |
738
|
|
|
/* |
739
|
|
|
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
740
|
|
|
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
741
|
|
|
$atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
742
|
|
|
$stco_entries_data_offset = 8; |
743
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
744
|
|
|
$atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stco_entries_data_offset, 4)); |
745
|
|
|
$stco_entries_data_offset += 4; |
746
|
|
|
} |
747
|
|
|
*/ break; |
748
|
|
|
|
749
|
|
|
case 'dref': // Data REFerence atom |
750
|
|
|
getid3_lib::ReadSequence( |
751
|
|
|
'BigEndian2Int', |
752
|
|
|
$atom_structure, |
753
|
|
|
$atom_data, |
754
|
|
|
0, |
755
|
|
|
[ |
756
|
|
|
'version' => 1, |
757
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
758
|
|
|
'number_entries' => 4 |
759
|
|
|
] |
760
|
|
|
); |
761
|
|
|
|
762
|
|
|
$dref_data_offset = 8; |
763
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
764
|
|
|
getid3_lib::ReadSequence( |
765
|
|
|
'BigEndian2Int', |
766
|
|
|
$atom_structure['data_references'][$i], |
767
|
|
|
$atom_data, |
768
|
|
|
$dref_data_offset, |
769
|
|
|
[ |
770
|
|
|
'size' => 4, |
771
|
|
|
'type' => -4, |
772
|
|
|
'version' => 1, |
773
|
|
|
'flags_raw' => 3 // hardcoded: 0x0000 |
774
|
|
|
] |
775
|
|
|
); |
776
|
|
|
$dref_data_offset += 12; |
777
|
|
|
|
778
|
|
|
$atom_structure['data_references'][$i]['data'] = substr($atom_data, $dref_data_offset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); |
779
|
|
|
$dref_data_offset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); |
780
|
|
|
|
781
|
|
|
$atom_structure['data_references'][$i]['flags']['self_reference'] = (bool)($atom_structure['data_references'][$i]['flags_raw'] & 0x001); |
782
|
|
|
} |
783
|
|
|
break; |
784
|
|
|
|
785
|
|
|
case 'gmin': // base Media INformation atom |
786
|
|
|
getid3_lib::ReadSequence( |
787
|
|
|
'BigEndian2Int', |
788
|
|
|
$atom_structure, |
789
|
|
|
$atom_data, |
790
|
|
|
0, |
791
|
|
|
[ |
792
|
|
|
'version' => 1, |
793
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
794
|
|
|
'graphics_mode' => 2, |
795
|
|
|
'opcolor_red' => 2, |
796
|
|
|
'opcolor_green' => 2, |
797
|
|
|
'opcolor_blue' => 2, |
798
|
|
|
'balance' => 2, |
799
|
|
|
'reserved' => 2 |
800
|
|
|
] |
801
|
|
|
); |
802
|
|
|
break; |
803
|
|
|
|
804
|
|
|
case 'smhd': // Sound Media information HeaDer atom |
805
|
|
|
getid3_lib::ReadSequence( |
806
|
|
|
'BigEndian2Int', |
807
|
|
|
$atom_structure, |
808
|
|
|
$atom_data, |
809
|
|
|
0, |
810
|
|
|
[ |
811
|
|
|
'version' => 1, |
812
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
813
|
|
|
'balance' => 2, |
814
|
|
|
'reserved' => 2 |
815
|
|
|
] |
816
|
|
|
); |
817
|
|
|
break; |
818
|
|
|
|
819
|
|
|
case 'vmhd': // Video Media information HeaDer atom |
820
|
|
|
getid3_lib::ReadSequence( |
821
|
|
|
'BigEndian2Int', |
822
|
|
|
$atom_structure, |
823
|
|
|
$atom_data, |
824
|
|
|
0, |
825
|
|
|
[ |
826
|
|
|
'version' => 1, |
827
|
|
|
'flags_raw' => 3, |
828
|
|
|
'graphics_mode' => 2, |
829
|
|
|
'opcolor_red' => 2, |
830
|
|
|
'opcolor_green' => 2, |
831
|
|
|
'opcolor_blue' => 2 |
832
|
|
|
] |
833
|
|
|
); |
834
|
|
|
$atom_structure['flags']['no_lean_ahead'] = (bool)($atom_structure['flags_raw'] & 0x001); |
835
|
|
|
break; |
836
|
|
|
|
837
|
|
|
case 'hdlr': // HanDLeR reference atom |
838
|
|
|
getid3_lib::ReadSequence( |
839
|
|
|
'BigEndian2Int', |
840
|
|
|
$atom_structure, |
841
|
|
|
$atom_data, |
842
|
|
|
0, |
843
|
|
|
[ |
844
|
|
|
'version' => 1, |
845
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
846
|
|
|
'component_type' => -4, |
847
|
|
|
'component_subtype' => -4, |
848
|
|
|
'component_manufacturer' => -4, |
849
|
|
|
'component_flags_raw' => 4, |
850
|
|
|
'component_flags_mask' => 4 |
851
|
|
|
] |
852
|
|
|
); |
853
|
|
|
|
854
|
|
|
$atom_structure['component_name'] = substr(substr($atom_data, 24), 1); /// Pascal2String |
855
|
|
|
|
856
|
|
|
if (('STpn' == $atom_structure['component_subtype']) && ('zzzz' == $atom_structure['component_manufacturer'])) { |
857
|
|
|
$info['video']['dataformat'] = 'quicktimevr'; |
858
|
|
|
} |
859
|
|
|
break; |
860
|
|
|
|
861
|
|
|
case 'mdhd': // MeDia HeaDer atom |
862
|
|
|
getid3_lib::ReadSequence( |
863
|
|
|
'BigEndian2Int', |
864
|
|
|
$atom_structure, |
865
|
|
|
$atom_data, |
866
|
|
|
0, |
867
|
|
|
[ |
868
|
|
|
'version' => 1, |
869
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
870
|
|
|
'creation_time' => 4, |
871
|
|
|
'modify_time' => 4, |
872
|
|
|
'time_scale' => 4, |
873
|
|
|
'duration' => 4, |
874
|
|
|
'language_id' => 2, |
875
|
|
|
'quality' => 2 |
876
|
|
|
] |
877
|
|
|
); |
878
|
|
|
|
879
|
|
|
if (0 == $atom_structure['time_scale']) { |
880
|
|
|
throw new getid3_exception('Corrupt Quicktime file: mdhd.time_scale == zero'); |
881
|
|
|
} |
882
|
|
|
$info_quicktime['time_scale'] = max(@$info['quicktime']['time_scale'], $atom_structure['time_scale']); |
883
|
|
|
|
884
|
|
|
$atom_structure['creation_time_unix'] = (int)($atom_structure['creation_time'] - 2082844800); // DateMac2Unix() |
885
|
|
|
$atom_structure['modify_time_unix'] = (int)($atom_structure['modify_time'] - 2082844800); // DateMac2Unix() |
886
|
|
|
$atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; |
887
|
|
|
$atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); |
888
|
|
|
if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { |
889
|
|
|
$info['comments']['language'][] = $atom_structure['language']; |
890
|
|
|
} |
891
|
|
|
break; |
892
|
|
|
|
893
|
|
|
case 'pnot': // Preview atom |
894
|
|
|
getid3_lib::ReadSequence( |
895
|
|
|
'BigEndian2Int', |
896
|
|
|
$atom_structure, |
897
|
|
|
$atom_data, |
898
|
|
|
0, |
899
|
|
|
[ |
900
|
|
|
'modification_date' => 4, // "standard Macintosh format" |
901
|
|
|
'version_number' => 2, // hardcoded: 0x00 |
902
|
|
|
'atom_type' => -4, // usually: 'PICT' |
903
|
|
|
'atom_index' => 2 // usually: 0x01 |
904
|
|
|
] |
905
|
|
|
); |
906
|
|
|
$atom_structure['modification_date_unix'] = (int)($atom_structure['modification_date'] - 2082844800); // DateMac2Unix() |
907
|
|
|
break; |
908
|
|
|
|
909
|
|
|
case 'crgn': // Clipping ReGioN atom |
910
|
|
|
$atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box, |
911
|
|
|
$atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields |
912
|
|
|
$atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. |
913
|
|
|
break; |
914
|
|
|
|
915
|
|
|
case 'load': // track LOAD settings atom |
916
|
|
|
getid3_lib::ReadSequence( |
917
|
|
|
'BigEndian2Int', |
918
|
|
|
$atom_structure, |
919
|
|
|
$atom_data, |
920
|
|
|
0, |
921
|
|
|
[ |
922
|
|
|
'preload_start_time' => 4, |
923
|
|
|
'preload_duration' => 4, |
924
|
|
|
'preload_flags_raw' => 4, |
925
|
|
|
'default_hints_raw' => 4 |
926
|
|
|
] |
927
|
|
|
); |
928
|
|
|
|
929
|
|
|
$atom_structure['default_hints']['double_buffer'] = (bool)($atom_structure['default_hints_raw'] & 0x0020); |
930
|
|
|
$atom_structure['default_hints']['high_quality'] = (bool)($atom_structure['default_hints_raw'] & 0x0100); |
931
|
|
|
break; |
932
|
|
|
|
933
|
|
|
case 'tmcd': // TiMe CoDe atom |
934
|
|
|
case 'chap': // CHAPter list atom |
935
|
|
|
case 'sync': // SYNChronization atom |
936
|
|
|
case 'scpt': // tranSCriPT atom |
937
|
|
|
case 'ssrc': // non-primary SouRCe atom |
938
|
|
|
for ($i = 0; $i < (strlen($atom_data) % 4); $i++) { |
939
|
|
|
$atom_structure['track_id'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $i * 4, 4)); |
940
|
|
|
} |
941
|
|
|
break; |
942
|
|
|
|
943
|
|
|
case 'elst': // Edit LiST atom |
944
|
|
|
getid3_lib::ReadSequence( |
945
|
|
|
'BigEndian2Int', |
946
|
|
|
$atom_structure, |
947
|
|
|
$atom_data, |
948
|
|
|
0, |
949
|
|
|
[ |
950
|
|
|
'version' => 1, |
951
|
|
|
'flags_raw' => 3, // hardcoded: 0x0000 |
952
|
|
|
'number_entries' => 4 |
953
|
|
|
] |
954
|
|
|
); |
955
|
|
|
|
956
|
|
|
for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
957
|
|
|
$atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4)); |
958
|
|
|
$atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4)); |
959
|
|
|
$atom_structure['edit_list'][$i]['media_rate'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4)); |
960
|
|
|
} |
961
|
|
|
break; |
962
|
|
|
|
963
|
|
|
case 'kmat': // compressed MATte atom |
964
|
|
|
$atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
965
|
|
|
$atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
966
|
|
|
$atom_structure['matte_data_raw'] = substr($atom_data, 4); |
967
|
|
|
break; |
968
|
|
|
|
969
|
|
|
case 'ctab': // Color TABle atom |
970
|
|
|
$atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000 |
971
|
|
|
$atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000 |
972
|
|
|
$atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1; |
973
|
|
|
for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) { |
974
|
|
|
$atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2)); |
975
|
|
|
$atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2)); |
976
|
|
|
$atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2)); |
977
|
|
|
$atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2)); |
978
|
|
|
} |
979
|
|
|
break; |
980
|
|
|
|
981
|
|
|
case 'mvhd': // MoVie HeaDer atom |
982
|
|
|
getid3_lib::ReadSequence( |
983
|
|
|
'BigEndian2Int', |
984
|
|
|
$atom_structure, |
985
|
|
|
$atom_data, |
986
|
|
|
0, |
987
|
|
|
[ |
988
|
|
|
'version' => 1, |
989
|
|
|
'flags_raw' => 3, |
990
|
|
|
'creation_time' => 4, |
991
|
|
|
'modify_time' => 4, |
992
|
|
|
'time_scale' => 4, |
993
|
|
|
'duration' => 4 |
994
|
|
|
] |
995
|
|
|
); |
996
|
|
|
|
997
|
|
|
$atom_structure['preferred_rate'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 20, 4)); |
998
|
|
|
$atom_structure['preferred_volume'] = getid3_quicktime::FixedPoint8_8(substr($atom_data, 24, 2)); |
999
|
|
|
$atom_structure['reserved'] = substr($atom_data, 26, 10); |
1000
|
|
|
$atom_structure['matrix_a'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 36, 4)); |
1001
|
|
|
$atom_structure['matrix_b'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 40, 4)); |
1002
|
|
|
$atom_structure['matrix_u'] = getid3_quicktime::FixedPoint2_30(substr($atom_data, 44, 4)); |
1003
|
|
|
$atom_structure['matrix_c'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 48, 4)); |
1004
|
|
|
$atom_structure['matrix_d'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 52, 4)); |
1005
|
|
|
$atom_structure['matrix_v'] = getid3_quicktime::FixedPoint2_30(substr($atom_data, 56, 4)); |
1006
|
|
|
$atom_structure['matrix_x'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 60, 4)); |
1007
|
|
|
$atom_structure['matrix_y'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 64, 4)); |
1008
|
|
|
$atom_structure['matrix_w'] = getid3_quicktime::FixedPoint2_30(substr($atom_data, 68, 4)); |
1009
|
|
|
|
1010
|
|
|
getid3_lib::ReadSequence( |
1011
|
|
|
'BigEndian2Int', |
1012
|
|
|
$atom_structure, |
1013
|
|
|
$atom_data, |
1014
|
|
|
72, |
1015
|
|
|
[ |
1016
|
|
|
'preview_time' => 4, |
1017
|
|
|
'preview_duration' => 4, |
1018
|
|
|
'poster_time' => 4, |
1019
|
|
|
'selection_time' => 4, |
1020
|
|
|
'selection_duration' => 4, |
1021
|
|
|
'current_time' => 4, |
1022
|
|
|
'next_track_id' => 4 |
1023
|
|
|
] |
1024
|
|
|
); |
1025
|
|
|
|
1026
|
|
|
if (0 == $atom_structure['time_scale']) { |
1027
|
|
|
throw new getid3_exception('Corrupt Quicktime file: mvhd.time_scale == zero'); |
1028
|
|
|
} |
1029
|
|
|
|
1030
|
|
|
$atom_structure['creation_time_unix'] = (int)($atom_structure['creation_time'] - 2082844800); // DateMac2Unix() |
1031
|
|
|
$atom_structure['modify_time_unix'] = (int)($atom_structure['modify_time'] - 2082844800); // DateMac2Unix() |
1032
|
|
|
$info_quicktime['time_scale'] = max(@$info['quicktime']['time_scale'], $atom_structure['time_scale']); |
1033
|
|
|
$info_quicktime['display_scale'] = $atom_structure['matrix_a']; |
1034
|
|
|
$info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; |
1035
|
|
|
break; |
1036
|
|
|
|
1037
|
|
|
case 'tkhd': // TracK HeaDer atom |
1038
|
|
|
getid3_lib::ReadSequence( |
1039
|
|
|
'BigEndian2Int', |
1040
|
|
|
$atom_structure, |
1041
|
|
|
$atom_data, |
1042
|
|
|
0, |
1043
|
|
|
[ |
1044
|
|
|
'version' => 1, |
1045
|
|
|
'flags_raw' => 3, |
1046
|
|
|
'creation_time' => 4, |
1047
|
|
|
'modify_time' => 4, |
1048
|
|
|
'trackid' => 4, |
1049
|
|
|
'reserved1' => 4, |
1050
|
|
|
'duration' => 4, |
1051
|
|
|
'reserved2' => 8, |
1052
|
|
|
'layer' => 2, |
1053
|
|
|
'alternate_group' => 2 |
1054
|
|
|
] |
1055
|
|
|
); |
1056
|
|
|
|
1057
|
|
|
$atom_structure['volume'] = getid3_quicktime::FixedPoint8_8(substr($atom_data, 36, 2)); |
1058
|
|
|
$atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2)); |
1059
|
|
|
$atom_structure['matrix_a'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 40, 4)); |
1060
|
|
|
$atom_structure['matrix_b'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 44, 4)); |
1061
|
|
|
$atom_structure['matrix_u'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 48, 4)); |
1062
|
|
|
$atom_structure['matrix_c'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 52, 4)); |
1063
|
|
|
$atom_structure['matrix_v'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 56, 4)); |
1064
|
|
|
$atom_structure['matrix_d'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 60, 4)); |
1065
|
|
|
$atom_structure['matrix_x'] = getid3_quicktime::FixedPoint2_30(substr($atom_data, 64, 4)); |
1066
|
|
|
$atom_structure['matrix_y'] = getid3_quicktime::FixedPoint2_30(substr($atom_data, 68, 4)); |
1067
|
|
|
$atom_structure['matrix_w'] = getid3_quicktime::FixedPoint2_30(substr($atom_data, 72, 4)); |
1068
|
|
|
$atom_structure['width'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 76, 4)); |
1069
|
|
|
$atom_structure['height'] = getid3_quicktime::FixedPoint16_16(substr($atom_data, 80, 4)); |
1070
|
|
|
|
1071
|
|
|
$atom_structure['flags']['enabled'] = (bool)($atom_structure['flags_raw'] & 0x0001); |
1072
|
|
|
$atom_structure['flags']['in_movie'] = (bool)($atom_structure['flags_raw'] & 0x0002); |
1073
|
|
|
$atom_structure['flags']['in_preview'] = (bool)($atom_structure['flags_raw'] & 0x0004); |
1074
|
|
|
$atom_structure['flags']['in_poster'] = (bool)($atom_structure['flags_raw'] & 0x0008); |
1075
|
|
|
$atom_structure['creation_time_unix'] = (int)($atom_structure['creation_time'] - 2082844800); // DateMac2Unix() |
1076
|
|
|
$atom_structure['modify_time_unix'] = (int)($atom_structure['modify_time'] - 2082844800); // DateMac2Unix() |
1077
|
|
|
|
1078
|
|
|
if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) { |
1079
|
|
|
$info['video']['resolution_x'] = $atom_structure['width']; |
1080
|
|
|
$info['video']['resolution_y'] = $atom_structure['height']; |
1081
|
|
|
} |
1082
|
|
|
|
1083
|
|
|
if (1 == $atom_structure['flags']['enabled']) { |
1084
|
|
|
$info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']); |
1085
|
|
|
$info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']); |
1086
|
|
|
} |
1087
|
|
|
|
1088
|
|
|
if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) { |
1089
|
|
|
$info_quicktime['video']['resolution_x'] = $info['video']['resolution_x']; |
1090
|
|
|
$info_quicktime['video']['resolution_y'] = $info['video']['resolution_y']; |
1091
|
|
|
} else { |
1092
|
|
|
unset($info['video']['resolution_x']); |
1093
|
|
|
unset($info['video']['resolution_y']); |
1094
|
|
|
unset($info_quicktime['video']); |
1095
|
|
|
} |
1096
|
|
|
break; |
1097
|
|
|
|
1098
|
|
|
case 'meta': // METAdata atom |
1099
|
|
|
// http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt |
1100
|
|
|
$next_tag_position = strpos($atom_data, '�'); |
1101
|
|
|
while ($next_tag_position < strlen($atom_data)) { |
1102
|
|
|
$meta_item_size = getid3_lib::BigEndian2Int(substr($atom_data, $next_tag_position - 4, 4)) - 4; |
1103
|
|
|
if (-4 == $meta_item_size) { |
1104
|
|
|
break; |
1105
|
|
|
} |
1106
|
|
|
$meta_item_raw = substr($atom_data, $next_tag_position, $meta_item_size); |
1107
|
|
|
$meta_item_key = substr($meta_item_raw, 0, 4); |
1108
|
|
|
$meta_item_data = substr($meta_item_raw, 20); |
1109
|
|
|
$next_tag_position += $meta_item_size + 4; |
1110
|
|
|
|
1111
|
|
|
$this->CopyToAppropriateCommentsSection($meta_item_key, $meta_item_data); |
1112
|
|
|
} |
1113
|
|
|
break; |
1114
|
|
|
|
1115
|
|
|
case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) |
1116
|
|
|
getid3_lib::ReadSequence( |
1117
|
|
|
'BigEndian2Int', |
1118
|
|
|
$atom_structure, |
1119
|
|
|
$atom_data, |
1120
|
|
|
0, |
1121
|
|
|
[ |
1122
|
|
|
'signature' => -4, |
1123
|
|
|
'unknown_1' => 4, |
1124
|
|
|
'fourcc' => -4, |
1125
|
|
|
] |
1126
|
|
|
); |
1127
|
|
|
break; |
1128
|
|
|
|
1129
|
|
|
case 'mdat': // Media DATa atom |
1130
|
|
|
case 'free': // FREE space atom |
1131
|
|
|
case 'skip': // SKIP atom |
1132
|
|
|
case 'wide': // 64-bit expansion placeholder atom |
1133
|
|
|
// 'mdat' data is too big to deal with, contains no useful metadata |
1134
|
|
|
// 'free', 'skip' and 'wide' are just padding, contains no useful data at all |
1135
|
|
|
|
1136
|
|
|
// When writing QuickTime files, it is sometimes necessary to update an atom's size. |
1137
|
|
|
// It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom |
1138
|
|
|
// is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime |
1139
|
|
|
// puts an 8-byte placeholder atom before any atoms it may have to update the size of. |
1140
|
|
|
// In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the |
1141
|
|
|
// placeholder atom can be overwritten to obtain the necessary 8 extra bytes. |
1142
|
|
|
// The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). |
1143
|
|
|
break; |
1144
|
|
|
|
1145
|
|
|
case 'nsav': // NoSAVe atom |
1146
|
|
|
// http://developer.apple.com/technotes/tn/tn2038.html |
1147
|
|
|
$atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
1148
|
|
|
break; |
1149
|
|
|
|
1150
|
|
|
case 'ctyp': // Controller TYPe atom (seen on QTVR) |
1151
|
|
|
// http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt |
1152
|
|
|
// some controller names are: |
1153
|
|
|
// 0x00 + 'std' for linear movie |
1154
|
|
|
// 'none' for no controls |
1155
|
|
|
$atom_structure['ctyp'] = substr($atom_data, 0, 4); |
1156
|
|
|
switch ($atom_structure['ctyp']) { |
1157
|
|
|
case 'qtvr': |
1158
|
|
|
$info['video']['dataformat'] = 'quicktimevr'; |
1159
|
|
|
break; |
1160
|
|
|
} |
1161
|
|
|
break; |
1162
|
|
|
|
1163
|
|
|
case 'pano': // PANOrama track (seen on QTVR) |
1164
|
|
|
$atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
1165
|
|
|
break; |
1166
|
|
|
|
1167
|
|
|
case 'hint': // HINT track |
1168
|
|
|
case 'hinf': // |
1169
|
|
|
case 'hinv': // |
1170
|
|
|
case 'hnti': // |
1171
|
|
|
$info['quicktime']['hinting'] = true; |
1172
|
|
|
break; |
1173
|
|
|
|
1174
|
|
|
case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) |
1175
|
|
|
for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) { |
1176
|
|
|
$atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); |
1177
|
|
|
} |
1178
|
|
|
break; |
1179
|
|
|
|
1180
|
|
|
case 'FXTC': // Something to do with Adobe After Effects (?) |
1181
|
|
|
case 'PrmA': |
1182
|
|
|
case 'code': |
1183
|
|
|
case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html |
1184
|
|
|
// Observed-but-not-handled atom types are just listed here |
1185
|
|
|
// to prevent warnings being generated |
1186
|
|
|
$atom_structure['data'] = $atom_data; |
1187
|
|
|
break; |
1188
|
|
|
|
1189
|
|
|
default: |
1190
|
|
|
$getid3->warning('Unknown QuickTime atom type: "' . $atom_name . '" at offset ' . $base_offset); |
1191
|
|
|
$atom_structure['data'] = $atom_data; |
1192
|
|
|
break; |
1193
|
|
|
} |
1194
|
|
|
array_pop($atom_hierarchy); |
1195
|
|
|
return $atom_structure; |
1196
|
|
|
} |
1197
|
|
|
|
1198
|
|
|
private function QuicktimeParseContainerAtom($atom_data, $base_offset, &$atom_hierarchy) |
1199
|
|
|
{ |
1200
|
|
|
if ((4 == strlen($atom_data)) && (0x00000000 == getid3_lib::BigEndian2Int($atom_data))) { |
1201
|
|
|
return false; |
1202
|
|
|
} |
1203
|
|
|
|
1204
|
|
|
$atom_structure = false; |
1205
|
|
|
$subatom_offset = 0; |
1206
|
|
|
|
1207
|
|
|
while ($subatom_offset < strlen($atom_data)) { |
1208
|
|
|
$subatom_size = getid3_lib::BigEndian2Int(substr($atom_data, $subatom_offset + 0, 4)); |
1209
|
|
|
$subatom_name = substr($atom_data, $subatom_offset + 4, 4); |
1210
|
|
|
$subatom_data = substr($atom_data, $subatom_offset + 8, $subatom_size - 8); |
1211
|
|
|
|
1212
|
|
|
if (0 == $subatom_size) { |
1213
|
|
|
// Furthermore, for historical reasons the list of atoms is optionally |
1214
|
|
|
// terminated by a 32-bit integer set to 0. If you are writing a program |
1215
|
|
|
// to read user data atoms, you should allow for the terminating 0. |
1216
|
|
|
return $atom_structure; |
1217
|
|
|
} |
1218
|
|
|
|
1219
|
|
|
$atom_structure[] = $this->QuicktimeParseAtom($subatom_name, $subatom_size, $subatom_data, $base_offset + $subatom_offset, $atom_hierarchy); |
1220
|
|
|
|
1221
|
|
|
$subatom_offset += $subatom_size; |
1222
|
|
|
} |
1223
|
|
|
return $atom_structure; |
1224
|
|
|
} |
1225
|
|
|
|
1226
|
|
|
private function CopyToAppropriateCommentsSection($key_name, $data) |
1227
|
|
|
{ |
1228
|
|
|
// http://www.geocities.com/xhelmboyx/quicktime/formats/qtm-layout.txt |
1229
|
|
|
|
1230
|
|
|
static $translator = [ |
1231
|
|
|
'�cpy' => 'copyright', |
1232
|
|
|
'�day' => 'creation_date', |
1233
|
|
|
'�dir' => 'director', |
1234
|
|
|
'�ed1' => 'edit1', |
1235
|
|
|
'�ed2' => 'edit2', |
1236
|
|
|
'�ed3' => 'edit3', |
1237
|
|
|
'�ed4' => 'edit4', |
1238
|
|
|
'�ed5' => 'edit5', |
1239
|
|
|
'�ed6' => 'edit6', |
1240
|
|
|
'�ed7' => 'edit7', |
1241
|
|
|
'�ed8' => 'edit8', |
1242
|
|
|
'�ed9' => 'edit9', |
1243
|
|
|
'�fmt' => 'format', |
1244
|
|
|
'�inf' => 'information', |
1245
|
|
|
'�prd' => 'producer', |
1246
|
|
|
'�prf' => 'performers', |
1247
|
|
|
'�req' => 'system_requirements', |
1248
|
|
|
'�src' => 'source_credit', |
1249
|
|
|
'�wrt' => 'writer', |
1250
|
|
|
'�nam' => 'title', |
1251
|
|
|
'�cmt' => 'comment', |
1252
|
|
|
'�wrn' => 'warning', |
1253
|
|
|
'�hst' => 'host_computer', |
1254
|
|
|
'�mak' => 'make', |
1255
|
|
|
'�mod' => 'model', |
1256
|
|
|
'�PRD' => 'product', |
1257
|
|
|
'�swr' => 'software', |
1258
|
|
|
'�aut' => 'author', |
1259
|
|
|
'�ART' => 'artist', |
1260
|
|
|
'�trk' => 'track', |
1261
|
|
|
'�alb' => 'album', |
1262
|
|
|
'�com' => 'comment', |
1263
|
|
|
'�gen' => 'genre', |
1264
|
|
|
'�ope' => 'composer', |
1265
|
|
|
'�url' => 'url', |
1266
|
|
|
'�enc' => 'encoder' |
1267
|
|
|
]; |
1268
|
|
|
|
1269
|
|
|
if (isset($translator[$key_name])) { |
1270
|
|
|
$this->getid3->info['quicktime']['comments'][$translator[$key_name]][] = $data; |
1271
|
|
|
} |
1272
|
|
|
|
1273
|
|
|
return true; |
1274
|
|
|
} |
1275
|
|
|
|
1276
|
|
|
public static function QuicktimeLanguageLookup($language_id) |
1277
|
|
|
{ |
1278
|
|
|
static $lookup = [ |
1279
|
|
|
0 => 'English', |
1280
|
|
|
1 => 'French', |
1281
|
|
|
2 => 'German', |
1282
|
|
|
3 => 'Italian', |
1283
|
|
|
4 => 'Dutch', |
1284
|
|
|
5 => 'Swedish', |
1285
|
|
|
6 => 'Spanish', |
1286
|
|
|
7 => 'Danish', |
1287
|
|
|
8 => 'Portuguese', |
1288
|
|
|
9 => 'Norwegian', |
1289
|
|
|
10 => 'Hebrew', |
1290
|
|
|
11 => 'Japanese', |
1291
|
|
|
12 => 'Arabic', |
1292
|
|
|
13 => 'Finnish', |
1293
|
|
|
14 => 'Greek', |
1294
|
|
|
15 => 'Icelandic', |
1295
|
|
|
16 => 'Maltese', |
1296
|
|
|
17 => 'Turkish', |
1297
|
|
|
18 => 'Croatian', |
1298
|
|
|
19 => 'Chinese (Traditional)', |
1299
|
|
|
20 => 'Urdu', |
1300
|
|
|
21 => 'Hindi', |
1301
|
|
|
22 => 'Thai', |
1302
|
|
|
23 => 'Korean', |
1303
|
|
|
24 => 'Lithuanian', |
1304
|
|
|
25 => 'Polish', |
1305
|
|
|
26 => 'Hungarian', |
1306
|
|
|
27 => 'Estonian', |
1307
|
|
|
28 => 'Lettish', |
1308
|
|
|
28 => 'Latvian', |
1309
|
|
|
29 => 'Saamisk', |
1310
|
|
|
29 => 'Lappish', |
1311
|
|
|
30 => 'Faeroese', |
1312
|
|
|
31 => 'Farsi', |
1313
|
|
|
31 => 'Persian', |
1314
|
|
|
32 => 'Russian', |
1315
|
|
|
33 => 'Chinese (Simplified)', |
1316
|
|
|
34 => 'Flemish', |
1317
|
|
|
35 => 'Irish', |
1318
|
|
|
36 => 'Albanian', |
1319
|
|
|
37 => 'Romanian', |
1320
|
|
|
38 => 'Czech', |
1321
|
|
|
39 => 'Slovak', |
1322
|
|
|
40 => 'Slovenian', |
1323
|
|
|
41 => 'Yiddish', |
1324
|
|
|
42 => 'Serbian', |
1325
|
|
|
43 => 'Macedonian', |
1326
|
|
|
44 => 'Bulgarian', |
1327
|
|
|
45 => 'Ukrainian', |
1328
|
|
|
46 => 'Byelorussian', |
1329
|
|
|
47 => 'Uzbek', |
1330
|
|
|
48 => 'Kazakh', |
1331
|
|
|
49 => 'Azerbaijani', |
1332
|
|
|
50 => 'AzerbaijanAr', |
1333
|
|
|
51 => 'Armenian', |
1334
|
|
|
52 => 'Georgian', |
1335
|
|
|
53 => 'Moldavian', |
1336
|
|
|
54 => 'Kirghiz', |
1337
|
|
|
55 => 'Tajiki', |
1338
|
|
|
56 => 'Turkmen', |
1339
|
|
|
57 => 'Mongolian', |
1340
|
|
|
58 => 'MongolianCyr', |
1341
|
|
|
59 => 'Pashto', |
1342
|
|
|
60 => 'Kurdish', |
1343
|
|
|
61 => 'Kashmiri', |
1344
|
|
|
62 => 'Sindhi', |
1345
|
|
|
63 => 'Tibetan', |
1346
|
|
|
64 => 'Nepali', |
1347
|
|
|
65 => 'Sanskrit', |
1348
|
|
|
66 => 'Marathi', |
1349
|
|
|
67 => 'Bengali', |
1350
|
|
|
68 => 'Assamese', |
1351
|
|
|
69 => 'Gujarati', |
1352
|
|
|
70 => 'Punjabi', |
1353
|
|
|
71 => 'Oriya', |
1354
|
|
|
72 => 'Malayalam', |
1355
|
|
|
73 => 'Kannada', |
1356
|
|
|
74 => 'Tamil', |
1357
|
|
|
75 => 'Telugu', |
1358
|
|
|
76 => 'Sinhalese', |
1359
|
|
|
77 => 'Burmese', |
1360
|
|
|
78 => 'Khmer', |
1361
|
|
|
79 => 'Lao', |
1362
|
|
|
80 => 'Vietnamese', |
1363
|
|
|
81 => 'Indonesian', |
1364
|
|
|
82 => 'Tagalog', |
1365
|
|
|
83 => 'MalayRoman', |
1366
|
|
|
84 => 'MalayArabic', |
1367
|
|
|
85 => 'Amharic', |
1368
|
|
|
86 => 'Tigrinya', |
1369
|
|
|
87 => 'Galla', |
1370
|
|
|
87 => 'Oromo', |
1371
|
|
|
88 => 'Somali', |
1372
|
|
|
89 => 'Swahili', |
1373
|
|
|
90 => 'Ruanda', |
1374
|
|
|
91 => 'Rundi', |
1375
|
|
|
92 => 'Chewa', |
1376
|
|
|
93 => 'Malagasy', |
1377
|
|
|
94 => 'Esperanto', |
1378
|
|
|
128 => 'Welsh', |
1379
|
|
|
129 => 'Basque', |
1380
|
|
|
130 => 'Catalan', |
1381
|
|
|
131 => 'Latin', |
1382
|
|
|
132 => 'Quechua', |
1383
|
|
|
133 => 'Guarani', |
1384
|
|
|
134 => 'Aymara', |
1385
|
|
|
135 => 'Tatar', |
1386
|
|
|
136 => 'Uighur', |
1387
|
|
|
137 => 'Dzongkha', |
1388
|
|
|
138 => 'JavaneseRom' |
1389
|
|
|
]; |
1390
|
|
|
|
1391
|
|
|
return (isset($lookup[$language_id]) ? $lookup[$language_id] : 'invalid'); |
1392
|
|
|
} |
1393
|
|
|
|
1394
|
|
|
public static function QuicktimeVideoCodecLookup($codec_id) |
1395
|
|
|
{ |
1396
|
|
|
static $lookup = [ |
1397
|
|
|
'3IVX' => '3ivx MPEG-4', |
1398
|
|
|
'3IV1' => '3ivx MPEG-4 v1', |
1399
|
|
|
'3IV2' => '3ivx MPEG-4 v2', |
1400
|
|
|
'avr ' => 'AVR-JPEG', |
1401
|
|
|
'base' => 'Base', |
1402
|
|
|
'WRLE' => 'BMP', |
1403
|
|
|
'cvid' => 'Cinepak', |
1404
|
|
|
'clou' => 'Cloud', |
1405
|
|
|
'cmyk' => 'CMYK', |
1406
|
|
|
'yuv2' => 'ComponentVideo', |
1407
|
|
|
'yuvu' => 'ComponentVideoSigned', |
1408
|
|
|
'yuvs' => 'ComponentVideoUnsigned', |
1409
|
|
|
'dvc ' => 'DVC-NTSC', |
1410
|
|
|
'dvcp' => 'DVC-PAL', |
1411
|
|
|
'dvpn' => 'DVCPro-NTSC', |
1412
|
|
|
'dvpp' => 'DVCPro-PAL', |
1413
|
|
|
'fire' => 'Fire', |
1414
|
|
|
'flic' => 'FLC', |
1415
|
|
|
'b48r' => '48RGB', |
1416
|
|
|
'gif ' => 'GIF', |
1417
|
|
|
'smc ' => 'Graphics', |
1418
|
|
|
'h261' => 'H261', |
1419
|
|
|
'h263' => 'H263', |
1420
|
|
|
'IV41' => 'Indeo4', |
1421
|
|
|
'jpeg' => 'JPEG', |
1422
|
|
|
'PNTG' => 'MacPaint', |
1423
|
|
|
'msvc' => 'Microsoft Video1', |
1424
|
|
|
'mjpa' => 'Motion JPEG-A', |
1425
|
|
|
'mjpb' => 'Motion JPEG-B', |
1426
|
|
|
'myuv' => 'MPEG YUV420', |
1427
|
|
|
'dmb1' => 'OpenDML JPEG', |
1428
|
|
|
'kpcd' => 'PhotoCD', |
1429
|
|
|
'8BPS' => 'Planar RGB', |
1430
|
|
|
'png ' => 'PNG', |
1431
|
|
|
'qdrw' => 'QuickDraw', |
1432
|
|
|
'qdgx' => 'QuickDrawGX', |
1433
|
|
|
'raw ' => 'RAW', |
1434
|
|
|
'.SGI' => 'SGI', |
1435
|
|
|
'b16g' => '16Gray', |
1436
|
|
|
'b64a' => '64ARGB', |
1437
|
|
|
'SVQ1' => 'Sorenson Video 1', |
1438
|
|
|
'SVQ1' => 'Sorenson Video 3', |
1439
|
|
|
'syv9' => 'Sorenson YUV9', |
1440
|
|
|
'tga ' => 'Targa', |
1441
|
|
|
'b32a' => '32AlphaGray', |
1442
|
|
|
'tiff' => 'TIFF', |
1443
|
|
|
'path' => 'Vector', |
1444
|
|
|
'rpza' => 'Video', |
1445
|
|
|
'ripl' => 'WaterRipple', |
1446
|
|
|
'WRAW' => 'Windows RAW', |
1447
|
|
|
'y420' => 'YUV420' |
1448
|
|
|
]; |
1449
|
|
|
|
1450
|
|
|
return (isset($lookup[$codec_id]) ? $lookup[$codec_id] : ''); |
1451
|
|
|
} |
1452
|
|
|
|
1453
|
|
|
public static function QuicktimeAudioCodecLookup($codec_id) |
1454
|
|
|
{ |
1455
|
|
|
static $lookup = [ |
1456
|
|
|
'.mp3' => 'Fraunhofer MPEG Layer-III alias', |
1457
|
|
|
'aac ' => 'ISO/IEC 14496-3 AAC', |
1458
|
|
|
'agsm' => 'Apple GSM 10:1', |
1459
|
|
|
'alac' => 'Apple Lossless Audio Codec', |
1460
|
|
|
'alaw' => 'A-law 2:1', |
1461
|
|
|
'conv' => 'Sample Format', |
1462
|
|
|
'dvca' => 'DV', |
1463
|
|
|
'dvi ' => 'DV 4:1', |
1464
|
|
|
'eqal' => 'Frequency Equalizer', |
1465
|
|
|
'fl32' => '32-bit Floating Point', |
1466
|
|
|
'fl64' => '64-bit Floating Point', |
1467
|
|
|
'ima4' => 'Interactive Multimedia Association 4:1', |
1468
|
|
|
'in24' => '24-bit Integer', |
1469
|
|
|
'in32' => '32-bit Integer', |
1470
|
|
|
'lpc ' => 'LPC 23:1', |
1471
|
|
|
'MAC3' => 'Macintosh Audio Compression/Expansion (MACE) 3:1', |
1472
|
|
|
'MAC6' => 'Macintosh Audio Compression/Expansion (MACE) 6:1', |
1473
|
|
|
'mixb' => '8-bit Mixer', |
1474
|
|
|
'mixw' => '16-bit Mixer', |
1475
|
|
|
'mp4a' => 'ISO/IEC 14496-3 AAC', |
1476
|
|
|
"MS'\x00\x02" => 'Microsoft ADPCM', |
1477
|
|
|
"MS'\x00\x11" => 'DV IMA', |
1478
|
|
|
"MS\x00\x55" => 'Fraunhofer MPEG Layer III', |
1479
|
|
|
'NONE' => 'No Encoding', |
1480
|
|
|
'Qclp' => 'Qualcomm PureVoice', |
1481
|
|
|
'QDM2' => 'QDesign Music 2', |
1482
|
|
|
'QDMC' => 'QDesign Music 1', |
1483
|
|
|
'ratb' => '8-bit Rate', |
1484
|
|
|
'ratw' => '16-bit Rate', |
1485
|
|
|
'raw ' => 'raw PCM', |
1486
|
|
|
'sour' => 'Sound Source', |
1487
|
|
|
'sowt' => 'signed/two\'s complement (Little Endian)', |
1488
|
|
|
'str1' => 'Iomega MPEG layer II', |
1489
|
|
|
'str2' => 'Iomega MPEG *layer II', |
1490
|
|
|
'str3' => 'Iomega MPEG **layer II', |
1491
|
|
|
'str4' => 'Iomega MPEG ***layer II', |
1492
|
|
|
'twos' => 'signed/two\'s complement (Big Endian)', |
1493
|
|
|
'ulaw' => 'mu-law 2:1', |
1494
|
|
|
]; |
1495
|
|
|
|
1496
|
|
|
return (isset($lookup[$codec_id]) ? $lookup[$codec_id] : ''); |
1497
|
|
|
} |
1498
|
|
|
|
1499
|
|
|
public static function QuicktimeDCOMLookup($compression_id) |
1500
|
|
|
{ |
1501
|
|
|
static $lookup = [ |
1502
|
|
|
'zlib' => 'ZLib Deflate', |
1503
|
|
|
'adec' => 'Apple Compression' |
1504
|
|
|
]; |
1505
|
|
|
|
1506
|
|
|
return (isset($lookup[$compression_id]) ? $lookup[$compression_id] : ''); |
1507
|
|
|
} |
1508
|
|
|
|
1509
|
|
|
public static function QuicktimeColorNameLookup($color_depth_id) |
1510
|
|
|
{ |
1511
|
|
|
static $lookup = [ |
1512
|
|
|
1 => '2-color (monochrome)', |
1513
|
|
|
2 => '4-color', |
1514
|
|
|
4 => '16-color', |
1515
|
|
|
8 => '256-color', |
1516
|
|
|
16 => 'thousands (16-bit color)', |
1517
|
|
|
24 => 'millions (24-bit color)', |
1518
|
|
|
32 => 'millions+ (32-bit color)', |
1519
|
|
|
33 => 'black & white', |
1520
|
|
|
34 => '4-gray', |
1521
|
|
|
36 => '16-gray', |
1522
|
|
|
40 => '256-gray', |
1523
|
|
|
]; |
1524
|
|
|
|
1525
|
|
|
return (isset($lookup[$color_depth_id]) ? $lookup[$color_depth_id] : 'invalid'); |
1526
|
|
|
} |
1527
|
|
|
|
1528
|
|
|
public static function NoNullString($null_terminated_string) |
1529
|
|
|
{ |
1530
|
|
|
// remove the single null terminator on null terminated strings |
1531
|
|
|
if ("\x00" === substr($null_terminated_string, strlen($null_terminated_string) - 1, 1)) { |
1532
|
|
|
return substr($null_terminated_string, 0, strlen($null_terminated_string) - 1); |
1533
|
|
|
} |
1534
|
|
|
|
1535
|
|
|
return $null_terminated_string; |
1536
|
|
|
} |
1537
|
|
|
|
1538
|
|
|
public static function FixedPoint8_8($raw_data) |
1539
|
|
|
{ |
1540
|
|
|
return getid3_lib::BigEndian2Int($raw_data{0}) + (float)(getid3_lib::BigEndian2Int($raw_data{1}) / 256); |
1541
|
|
|
} |
1542
|
|
|
|
1543
|
|
|
public static function FixedPoint16_16($raw_data) |
1544
|
|
|
{ |
1545
|
|
|
return getid3_lib::BigEndian2Int(substr($raw_data, 0, 2)) + (float)(getid3_lib::BigEndian2Int(substr($raw_data, 2, 2)) / 65536); |
1546
|
|
|
} |
1547
|
|
|
|
1548
|
|
|
public static function FixedPoint2_30($raw_data) |
1549
|
|
|
{ |
1550
|
|
|
$binary_string = getid3_lib::BigEndian2Bin($raw_data); |
1551
|
|
|
return bindec(substr($binary_string, 0, 2)) + (float)(bindec(substr($binary_string, 2, 30)) / 1073741824); |
1552
|
|
|
} |
1553
|
|
|
|
1554
|
|
|
} |
1555
|
|
|
|
1556
|
|
|
|
1557
|
|
|
|
If an expression can have both
false
, andnull
as possible values. It is generally a good practice to always use strict comparison to clearly distinguish between those two values.