191 $info['audio']['dataformat'] = 'quicktime'; |
208 $info['audio']['dataformat'] = 'quicktime'; |
192 } |
209 } |
193 if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) { |
210 if (empty($info['video']['dataformat']) && !empty($info['quicktime']['video'])) { |
194 $info['video']['dataformat'] = 'quicktime'; |
211 $info['video']['dataformat'] = 'quicktime'; |
195 } |
212 } |
|
213 if (isset($info['video']) && ($info['mime_type'] == 'audio/mp4') && empty($info['video']['resolution_x']) && empty($info['video']['resolution_y'])) { |
|
214 unset($info['video']); |
|
215 } |
196 |
216 |
197 return true; |
217 return true; |
198 } |
218 } |
199 |
219 |
|
220 /** |
|
221 * @param string $atomname |
|
222 * @param int $atomsize |
|
223 * @param string $atom_data |
|
224 * @param int $baseoffset |
|
225 * @param array $atomHierarchy |
|
226 * @param bool $ParseAllPossibleAtoms |
|
227 * |
|
228 * @return array|false |
|
229 */ |
200 public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { |
230 public function QuicktimeParseAtom($atomname, $atomsize, $atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { |
201 // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm |
231 // http://developer.apple.com/techpubs/quicktime/qtdevdocs/APIREF/INDEX/atomalphaindex.htm |
202 // https://code.google.com/p/mp4v2/wiki/iTunesMetadata |
232 // https://code.google.com/p/mp4v2/wiki/iTunesMetadata |
203 |
233 |
204 $info = &$this->getid3->info; |
234 $info = &$this->getid3->info; |
205 |
235 |
206 $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see http://www.getid3.org/phpBB3/viewtopic.php?t=1717 |
236 $atom_parent = end($atomHierarchy); // not array_pop($atomHierarchy); see https://www.getid3.org/phpBB3/viewtopic.php?t=1717 |
207 array_push($atomHierarchy, $atomname); |
237 array_push($atomHierarchy, $atomname); |
|
238 $atom_structure = array(); |
208 $atom_structure['hierarchy'] = implode(' ', $atomHierarchy); |
239 $atom_structure['hierarchy'] = implode(' ', $atomHierarchy); |
209 $atom_structure['name'] = $atomname; |
240 $atom_structure['name'] = $atomname; |
210 $atom_structure['size'] = $atomsize; |
241 $atom_structure['size'] = $atomsize; |
211 $atom_structure['offset'] = $baseoffset; |
242 $atom_structure['offset'] = $baseoffset; |
212 switch ($atomname) { |
243 if (substr($atomname, 0, 3) == "\x00\x00\x00") { |
213 case 'moov': // MOVie container atom |
244 // https://github.com/JamesHeinrich/getID3/issues/139 |
214 case 'trak': // TRAcK container atom |
245 $atomname = getid3_lib::BigEndian2Int($atomname); |
215 case 'clip': // CLIPping container atom |
246 $atom_structure['name'] = $atomname; |
216 case 'matt': // track MATTe container atom |
247 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
217 case 'edts': // EDiTS container atom |
248 } else { |
218 case 'tref': // Track REFerence container atom |
249 switch ($atomname) { |
219 case 'mdia': // MeDIA container atom |
250 case 'moov': // MOVie container atom |
220 case 'minf': // Media INFormation container atom |
251 case 'trak': // TRAcK container atom |
221 case 'dinf': // Data INFormation container atom |
252 case 'clip': // CLIPping container atom |
222 case 'udta': // User DaTA container atom |
253 case 'matt': // track MATTe container atom |
223 case 'cmov': // Compressed MOVie container atom |
254 case 'edts': // EDiTS container atom |
224 case 'rmra': // Reference Movie Record Atom |
255 case 'tref': // Track REFerence container atom |
225 case 'rmda': // Reference Movie Descriptor Atom |
256 case 'mdia': // MeDIA container atom |
226 case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) |
257 case 'minf': // Media INFormation container atom |
227 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
258 case 'dinf': // Data INFormation container atom |
228 break; |
259 case 'nmhd': // Null Media HeaDer container atom |
229 |
260 case 'udta': // User DaTA container atom |
230 case 'ilst': // Item LiST container atom |
261 case 'cmov': // Compressed MOVie container atom |
231 if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) { |
262 case 'rmra': // Reference Movie Record Atom |
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 |
263 case 'rmda': // Reference Movie Descriptor Atom |
233 $allnumericnames = true; |
264 case 'gmhd': // Generic Media info HeaDer atom (seen on QTVR) |
234 foreach ($atom_structure['subatoms'] as $subatomarray) { |
265 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
235 if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) { |
266 break; |
236 $allnumericnames = false; |
267 |
237 break; |
268 case 'ilst': // Item LiST container atom |
238 } |
269 if ($atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms)) { |
239 } |
270 // some "ilst" atoms contain data atoms that have a numeric name, and the data is far more accessible if the returned array is compacted |
240 if ($allnumericnames) { |
271 $allnumericnames = true; |
241 $newData = array(); |
|
242 foreach ($atom_structure['subatoms'] as $subatomarray) { |
272 foreach ($atom_structure['subatoms'] as $subatomarray) { |
243 foreach ($subatomarray['subatoms'] as $newData_subatomarray) { |
273 if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) { |
244 unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']); |
274 $allnumericnames = false; |
245 $newData[$subatomarray['name']] = $newData_subatomarray; |
|
246 break; |
275 break; |
247 } |
276 } |
248 } |
277 } |
249 $atom_structure['data'] = $newData; |
278 if ($allnumericnames) { |
250 unset($atom_structure['subatoms']); |
279 $newData = array(); |
251 } |
280 foreach ($atom_structure['subatoms'] as $subatomarray) { |
252 } |
281 foreach ($subatomarray['subatoms'] as $newData_subatomarray) { |
253 break; |
282 unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']); |
254 |
283 $newData[$subatomarray['name']] = $newData_subatomarray; |
255 case "\x00\x00\x00\x01": |
284 break; |
256 case "\x00\x00\x00\x02": |
285 } |
257 case "\x00\x00\x00\x03": |
286 } |
258 case "\x00\x00\x00\x04": |
287 $atom_structure['data'] = $newData; |
259 case "\x00\x00\x00\x05": |
288 unset($atom_structure['subatoms']); |
260 $atomname = getid3_lib::BigEndian2Int($atomname); |
289 } |
261 $atom_structure['name'] = $atomname; |
290 } |
262 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
291 break; |
263 break; |
292 |
264 |
293 case 'stbl': // Sample TaBLe container atom |
265 case 'stbl': // Sample TaBLe container atom |
294 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
266 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
295 $isVideo = false; |
267 $isVideo = false; |
296 $framerate = 0; |
268 $framerate = 0; |
297 $framecount = 0; |
269 $framecount = 0; |
298 foreach ($atom_structure['subatoms'] as $key => $value_array) { |
270 foreach ($atom_structure['subatoms'] as $key => $value_array) { |
299 if (isset($value_array['sample_description_table'])) { |
271 if (isset($value_array['sample_description_table'])) { |
300 foreach ($value_array['sample_description_table'] as $key2 => $value_array2) { |
272 foreach ($value_array['sample_description_table'] as $key2 => $value_array2) { |
301 if (isset($value_array2['data_format'])) { |
273 if (isset($value_array2['data_format'])) { |
302 switch ($value_array2['data_format']) { |
274 switch ($value_array2['data_format']) { |
303 case 'avc1': |
275 case 'avc1': |
304 case 'mp4v': |
276 case 'mp4v': |
305 // video data |
277 // video data |
306 $isVideo = true; |
278 $isVideo = true; |
307 break; |
279 break; |
308 case 'mp4a': |
280 case 'mp4a': |
309 // audio data |
281 // audio data |
310 break; |
282 break; |
311 } |
|
312 } |
|
313 } |
|
314 } elseif (isset($value_array['time_to_sample_table'])) { |
|
315 foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) { |
|
316 if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) { |
|
317 $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3); |
|
318 $framecount = $value_array2['sample_count']; |
283 } |
319 } |
284 } |
320 } |
285 } |
321 } |
286 } elseif (isset($value_array['time_to_sample_table'])) { |
322 } |
287 foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) { |
323 if ($isVideo && $framerate) { |
288 if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) { |
324 $info['quicktime']['video']['frame_rate'] = $framerate; |
289 $framerate = round($info['quicktime']['time_scale'] / $value_array2['sample_duration'], 3); |
325 $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate']; |
290 $framecount = $value_array2['sample_count']; |
326 } |
291 } |
327 if ($isVideo && $framecount) { |
292 } |
328 $info['quicktime']['video']['frame_count'] = $framecount; |
293 } |
329 } |
294 } |
330 break; |
295 if ($isVideo && $framerate) { |
331 |
296 $info['quicktime']['video']['frame_rate'] = $framerate; |
332 |
297 $info['video']['frame_rate'] = $info['quicktime']['video']['frame_rate']; |
333 case "\xA9".'alb': // ALBum |
298 } |
334 case "\xA9".'ART': // |
299 if ($isVideo && $framecount) { |
335 case "\xA9".'art': // ARTist |
300 $info['quicktime']['video']['frame_count'] = $framecount; |
336 case "\xA9".'aut': // |
301 } |
337 case "\xA9".'cmt': // CoMmenT |
302 break; |
338 case "\xA9".'com': // COMposer |
303 |
339 case "\xA9".'cpy': // |
304 |
340 case "\xA9".'day': // content created year |
305 case "\xA9".'alb': // ALBum |
341 case "\xA9".'dir': // |
306 case "\xA9".'ART': // |
342 case "\xA9".'ed1': // |
307 case "\xA9".'art': // ARTist |
343 case "\xA9".'ed2': // |
308 case "\xA9".'aut': // |
344 case "\xA9".'ed3': // |
309 case "\xA9".'cmt': // CoMmenT |
345 case "\xA9".'ed4': // |
310 case "\xA9".'com': // COMposer |
346 case "\xA9".'ed5': // |
311 case "\xA9".'cpy': // |
347 case "\xA9".'ed6': // |
312 case "\xA9".'day': // content created year |
348 case "\xA9".'ed7': // |
313 case "\xA9".'dir': // |
349 case "\xA9".'ed8': // |
314 case "\xA9".'ed1': // |
350 case "\xA9".'ed9': // |
315 case "\xA9".'ed2': // |
351 case "\xA9".'enc': // |
316 case "\xA9".'ed3': // |
352 case "\xA9".'fmt': // |
317 case "\xA9".'ed4': // |
353 case "\xA9".'gen': // GENre |
318 case "\xA9".'ed5': // |
354 case "\xA9".'grp': // GRouPing |
319 case "\xA9".'ed6': // |
355 case "\xA9".'hst': // |
320 case "\xA9".'ed7': // |
356 case "\xA9".'inf': // |
321 case "\xA9".'ed8': // |
357 case "\xA9".'lyr': // LYRics |
322 case "\xA9".'ed9': // |
358 case "\xA9".'mak': // |
323 case "\xA9".'enc': // |
359 case "\xA9".'mod': // |
324 case "\xA9".'fmt': // |
360 case "\xA9".'nam': // full NAMe |
325 case "\xA9".'gen': // GENre |
361 case "\xA9".'ope': // |
326 case "\xA9".'grp': // GRouPing |
362 case "\xA9".'PRD': // |
327 case "\xA9".'hst': // |
363 case "\xA9".'prf': // |
328 case "\xA9".'inf': // |
364 case "\xA9".'req': // |
329 case "\xA9".'lyr': // LYRics |
365 case "\xA9".'src': // |
330 case "\xA9".'mak': // |
366 case "\xA9".'swr': // |
331 case "\xA9".'mod': // |
367 case "\xA9".'too': // encoder |
332 case "\xA9".'nam': // full NAMe |
368 case "\xA9".'trk': // TRacK |
333 case "\xA9".'ope': // |
369 case "\xA9".'url': // |
334 case "\xA9".'PRD': // |
370 case "\xA9".'wrn': // |
335 case "\xA9".'prf': // |
371 case "\xA9".'wrt': // WRiTer |
336 case "\xA9".'req': // |
372 case '----': // itunes specific |
337 case "\xA9".'src': // |
373 case 'aART': // Album ARTist |
338 case "\xA9".'swr': // |
374 case 'akID': // iTunes store account type |
339 case "\xA9".'too': // encoder |
375 case 'apID': // Purchase Account |
340 case "\xA9".'trk': // TRacK |
376 case 'atID': // |
341 case "\xA9".'url': // |
377 case 'catg': // CaTeGory |
342 case "\xA9".'wrn': // |
378 case 'cmID': // |
343 case "\xA9".'wrt': // WRiTer |
379 case 'cnID': // |
344 case '----': // itunes specific |
380 case 'covr': // COVeR artwork |
345 case 'aART': // Album ARTist |
381 case 'cpil': // ComPILation |
346 case 'akID': // iTunes store account type |
382 case 'cprt': // CoPyRighT |
347 case 'apID': // Purchase Account |
383 case 'desc': // DESCription |
348 case 'atID': // |
384 case 'disk': // DISK number |
349 case 'catg': // CaTeGory |
385 case 'egid': // Episode Global ID |
350 case 'cmID': // |
386 case 'geID': // |
351 case 'cnID': // |
387 case 'gnre': // GeNRE |
352 case 'covr': // COVeR artwork |
388 case 'hdvd': // HD ViDeo |
353 case 'cpil': // ComPILation |
389 case 'keyw': // KEYWord |
354 case 'cprt': // CoPyRighT |
390 case 'ldes': // Long DEScription |
355 case 'desc': // DESCription |
391 case 'pcst': // PodCaST |
356 case 'disk': // DISK number |
392 case 'pgap': // GAPless Playback |
357 case 'egid': // Episode Global ID |
393 case 'plID': // |
358 case 'geID': // |
394 case 'purd': // PURchase Date |
359 case 'gnre': // GeNRE |
395 case 'purl': // Podcast URL |
360 case 'hdvd': // HD ViDeo |
396 case 'rati': // |
361 case 'keyw': // KEYWord |
397 case 'rndu': // |
362 case 'ldes': // Long DEScription |
398 case 'rpdu': // |
363 case 'pcst': // PodCaST |
399 case 'rtng': // RaTiNG |
364 case 'pgap': // GAPless Playback |
400 case 'sfID': // iTunes store country |
365 case 'plID': // |
401 case 'soaa': // SOrt Album Artist |
366 case 'purd': // PURchase Date |
402 case 'soal': // SOrt ALbum |
367 case 'purl': // Podcast URL |
403 case 'soar': // SOrt ARtist |
368 case 'rati': // |
404 case 'soco': // SOrt COmposer |
369 case 'rndu': // |
405 case 'sonm': // SOrt NaMe |
370 case 'rpdu': // |
406 case 'sosn': // SOrt Show Name |
371 case 'rtng': // RaTiNG |
407 case 'stik': // |
372 case 'sfID': // iTunes store country |
408 case 'tmpo': // TeMPO (BPM) |
373 case 'soaa': // SOrt Album Artist |
409 case 'trkn': // TRacK Number |
374 case 'soal': // SOrt ALbum |
410 case 'tven': // tvEpisodeID |
375 case 'soar': // SOrt ARtist |
411 case 'tves': // TV EpiSode |
376 case 'soco': // SOrt COmposer |
412 case 'tvnn': // TV Network Name |
377 case 'sonm': // SOrt NaMe |
413 case 'tvsh': // TV SHow Name |
378 case 'sosn': // SOrt Show Name |
414 case 'tvsn': // TV SeasoN |
379 case 'stik': // |
415 if ($atom_parent == 'udta') { |
380 case 'tmpo': // TeMPO (BPM) |
416 // User data atom handler |
381 case 'trkn': // TRacK Number |
417 $atom_structure['data_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); |
382 case 'tven': // tvEpisodeID |
418 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); |
383 case 'tves': // TV EpiSode |
419 $atom_structure['data'] = substr($atom_data, 4); |
384 case 'tvnn': // TV Network Name |
420 |
385 case 'tvsh': // TV SHow Name |
421 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); |
386 case 'tvsn': // TV SeasoN |
422 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { |
387 if ($atom_parent == 'udta') { |
423 $info['comments']['language'][] = $atom_structure['language']; |
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 } |
424 } else { |
425 } else { |
425 while ($atomoffset < strlen($atom_data)) { |
426 // Apple item list box atom handler |
426 $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4)); |
427 $atomoffset = 0; |
427 $boxtype = substr($atom_data, $atomoffset + 4, 4); |
428 if (substr($atom_data, 2, 2) == "\x10\xB5") { |
428 $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8); |
429 // not sure what it means, but observed on iPhone4 data. |
429 if ($boxsize <= 1) { |
430 // Each $atom_data has 2 bytes of datasize, plus 0x10B5, then data |
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 while ($atomoffset < strlen($atom_data)) { |
431 $atom_structure['data'] = null; |
432 $boxsmallsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 2)); |
432 $atomoffset = strlen($atom_data); |
433 $boxsmalltype = substr($atom_data, $atomoffset + 2, 2); |
433 break; |
434 $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize); |
|
435 if ($boxsmallsize <= 1) { |
|
436 $this->warning('Invalid QuickTime atom smallbox size "'.$boxsmallsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset)); |
|
437 $atom_structure['data'] = null; |
|
438 $atomoffset = strlen($atom_data); |
|
439 break; |
|
440 } |
|
441 switch ($boxsmalltype) { |
|
442 case "\x10\xB5": |
|
443 $atom_structure['data'] = $boxsmalldata; |
|
444 break; |
|
445 default: |
|
446 $this->warning('Unknown QuickTime smallbox type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxsmalltype).'" ('.trim(getid3_lib::PrintHexBytes($boxsmalltype)).') at offset '.$baseoffset); |
|
447 $atom_structure['data'] = $atom_data; |
|
448 break; |
|
449 } |
|
450 $atomoffset += (4 + $boxsmallsize); |
434 } |
451 } |
435 $atomoffset += $boxsize; |
452 } else { |
436 |
453 while ($atomoffset < strlen($atom_data)) { |
437 switch ($boxtype) { |
454 $boxsize = getid3_lib::BigEndian2Int(substr($atom_data, $atomoffset, 4)); |
438 case 'mean': |
455 $boxtype = substr($atom_data, $atomoffset + 4, 4); |
439 case 'name': |
456 $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8); |
440 $atom_structure[$boxtype] = substr($boxdata, 4); |
457 if ($boxsize <= 1) { |
|
458 $this->warning('Invalid QuickTime atom box size "'.$boxsize.'" in atom "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" at offset: '.($atom_structure['offset'] + $atomoffset)); |
|
459 $atom_structure['data'] = null; |
|
460 $atomoffset = strlen($atom_data); |
441 break; |
461 break; |
442 |
462 } |
443 case 'data': |
463 $atomoffset += $boxsize; |
444 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1)); |
464 |
445 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3)); |
465 switch ($boxtype) { |
446 switch ($atom_structure['flags_raw']) { |
466 case 'mean': |
447 case 0: // data flag |
467 case 'name': |
448 case 21: // tmpo/cpil flag |
468 $atom_structure[$boxtype] = substr($boxdata, 4); |
449 switch ($atomname) { |
469 break; |
450 case 'cpil': |
470 |
451 case 'hdvd': |
471 case 'data': |
452 case 'pcst': |
472 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($boxdata, 0, 1)); |
453 case 'pgap': |
473 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($boxdata, 1, 3)); |
454 // 8-bit integer (boolean) |
474 switch ($atom_structure['flags_raw']) { |
455 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); |
475 case 0: // data flag |
456 break; |
476 case 21: // tmpo/cpil flag |
457 |
477 switch ($atomname) { |
458 case 'tmpo': |
478 case 'cpil': |
459 // 16-bit integer |
479 case 'hdvd': |
460 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); |
480 case 'pcst': |
461 break; |
481 case 'pgap': |
462 |
482 // 8-bit integer (boolean) |
463 case 'disk': |
483 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); |
464 case 'trkn': |
484 break; |
465 // binary |
485 |
466 $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2)); |
486 case 'tmpo': |
467 $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2)); |
487 // 16-bit integer |
468 $atom_structure['data'] = empty($num) ? '' : $num; |
488 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 2)); |
469 $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total; |
489 break; |
470 break; |
490 |
471 |
491 case 'disk': |
472 case 'gnre': |
492 case 'trkn': |
473 // enum |
493 // binary |
474 $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); |
494 $num = getid3_lib::BigEndian2Int(substr($boxdata, 10, 2)); |
475 $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); |
495 $num_total = getid3_lib::BigEndian2Int(substr($boxdata, 12, 2)); |
476 break; |
496 $atom_structure['data'] = empty($num) ? '' : $num; |
477 |
497 $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total; |
478 case 'rtng': |
498 break; |
479 // 8-bit integer |
499 |
480 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); |
500 case 'gnre': |
481 $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); |
501 // enum |
482 break; |
502 $GenreID = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); |
483 |
503 $atom_structure['data'] = getid3_id3v1::LookupGenreName($GenreID - 1); |
484 case 'stik': |
504 break; |
485 // 8-bit integer (enum) |
505 |
486 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); |
506 case 'rtng': |
487 $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); |
507 // 8-bit integer |
488 break; |
508 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); |
489 |
509 $atom_structure['data'] = $this->QuicktimeContentRatingLookup($atom_structure[$atomname]); |
490 case 'sfID': |
510 break; |
491 // 32-bit integer |
511 |
492 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); |
512 case 'stik': |
493 $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]); |
513 // 8-bit integer (enum) |
494 break; |
514 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 1)); |
495 |
515 $atom_structure['data'] = $this->QuicktimeSTIKLookup($atom_structure[$atomname]); |
496 case 'egid': |
516 break; |
497 case 'purl': |
517 |
498 $atom_structure['data'] = substr($boxdata, 8); |
518 case 'sfID': |
499 break; |
519 // 32-bit integer |
500 |
520 $atom_structure[$atomname] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); |
501 case 'plID': |
521 $atom_structure['data'] = $this->QuicktimeStoreFrontCodeLookup($atom_structure[$atomname]); |
502 // 64-bit integer |
522 break; |
503 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8)); |
523 |
504 break; |
524 case 'egid': |
505 |
525 case 'purl': |
506 case 'covr': |
526 $atom_structure['data'] = substr($boxdata, 8); |
507 $atom_structure['data'] = substr($boxdata, 8); |
527 break; |
|
528 |
|
529 case 'plID': |
|
530 // 64-bit integer |
|
531 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 8)); |
|
532 break; |
|
533 |
|
534 case 'covr': |
|
535 $atom_structure['data'] = substr($boxdata, 8); |
|
536 // not a foolproof check, but better than nothing |
|
537 if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) { |
|
538 $atom_structure['image_mime'] = 'image/jpeg'; |
|
539 } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) { |
|
540 $atom_structure['image_mime'] = 'image/png'; |
|
541 } elseif (preg_match('#^GIF#', $atom_structure['data'])) { |
|
542 $atom_structure['image_mime'] = 'image/gif'; |
|
543 } |
|
544 $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover'); |
|
545 break; |
|
546 |
|
547 case 'atID': |
|
548 case 'cnID': |
|
549 case 'geID': |
|
550 case 'tves': |
|
551 case 'tvsn': |
|
552 default: |
|
553 // 32-bit integer |
|
554 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($boxdata, 8, 4)); |
|
555 } |
|
556 break; |
|
557 |
|
558 case 1: // text flag |
|
559 case 13: // image flag |
|
560 default: |
|
561 $atom_structure['data'] = substr($boxdata, 8); |
|
562 if ($atomname == 'covr') { |
508 // not a foolproof check, but better than nothing |
563 // not a foolproof check, but better than nothing |
509 if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) { |
564 if (preg_match('#^\\xFF\\xD8\\xFF#', $atom_structure['data'])) { |
510 $atom_structure['image_mime'] = 'image/jpeg'; |
565 $atom_structure['image_mime'] = 'image/jpeg'; |
511 } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) { |
566 } elseif (preg_match('#^\\x89\\x50\\x4E\\x47\\x0D\\x0A\\x1A\\x0A#', $atom_structure['data'])) { |
512 $atom_structure['image_mime'] = 'image/png'; |
567 $atom_structure['image_mime'] = 'image/png'; |
513 } elseif (preg_match('#^GIF#', $atom_structure['data'])) { |
568 } elseif (preg_match('#^GIF#', $atom_structure['data'])) { |
514 $atom_structure['image_mime'] = 'image/gif'; |
569 $atom_structure['image_mime'] = 'image/gif'; |
515 } |
570 } |
516 break; |
571 $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_structure['data'], 'description'=>'cover'); |
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 } |
572 } |
542 } |
573 break; |
543 break; |
574 |
544 |
575 } |
545 } |
576 break; |
546 break; |
577 |
547 |
578 default: |
548 default: |
579 $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset); |
549 $this->warning('Unknown QuickTime box type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $boxtype).'" ('.trim(getid3_lib::PrintHexBytes($boxtype)).') at offset '.$baseoffset); |
580 $atom_structure['data'] = $atom_data; |
550 $atom_structure['data'] = $atom_data; |
581 |
551 |
582 } |
552 } |
583 } |
553 } |
584 } |
554 } |
585 } |
555 } |
586 $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']); |
556 $this->CopyToAppropriateCommentsSection($atomname, $atom_structure['data'], $atom_structure['name']); |
587 break; |
557 break; |
588 |
558 |
589 |
559 |
590 case 'play': // auto-PLAY atom |
560 case 'play': // auto-PLAY atom |
591 $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
561 $atom_structure['autoplay'] = (bool) getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
592 |
562 |
593 $info['quicktime']['autoplay'] = $atom_structure['autoplay']; |
563 $info['quicktime']['autoplay'] = $atom_structure['autoplay']; |
594 break; |
564 break; |
595 |
565 |
596 |
566 |
597 case 'WLOC': // Window LOCation atom |
567 case 'WLOC': // Window LOCation atom |
598 $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); |
568 $atom_structure['location_x'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); |
599 $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); |
569 $atom_structure['location_y'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); |
600 break; |
570 break; |
601 |
571 |
602 |
572 |
603 case 'LOOP': // LOOPing atom |
573 case 'LOOP': // LOOPing atom |
604 case 'SelO': // play SELection Only atom |
574 case 'SelO': // play SELection Only atom |
605 case 'AllF': // play ALL Frames atom |
575 case 'AllF': // play ALL Frames atom |
606 $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data); |
576 $atom_structure['data'] = getid3_lib::BigEndian2Int($atom_data); |
607 break; |
577 break; |
608 |
578 |
609 |
579 |
610 case 'name': // |
580 case 'name': // |
611 case 'MCPS': // Media Cleaner PRo |
581 case 'MCPS': // Media Cleaner PRo |
612 case '@PRM': // adobe PReMiere version |
582 case '@PRM': // adobe PReMiere version |
613 case '@PRQ': // adobe PRemiere Quicktime version |
583 case '@PRQ': // adobe PRemiere Quicktime version |
614 $atom_structure['data'] = $atom_data; |
584 $atom_structure['data'] = $atom_data; |
615 break; |
585 break; |
616 |
586 |
617 |
587 |
618 case 'cmvd': // Compressed MooV Data atom |
588 case 'cmvd': // Compressed MooV Data atom |
619 // Code by ubergeekØubergeek*tv based on information from |
589 // Code by ubergeekØubergeek*tv based on information from |
620 // http://developer.apple.com/quicktime/icefloe/dispatch012.html |
590 // http://developer.apple.com/quicktime/icefloe/dispatch012.html |
621 $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
591 $atom_structure['unCompressedSize'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
622 |
592 |
623 $CompressedFileData = substr($atom_data, 4); |
593 $CompressedFileData = substr($atom_data, 4); |
624 if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { |
594 if ($UncompressedHeader = @gzuncompress($CompressedFileData)) { |
625 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($UncompressedHeader, 0, $atomHierarchy, $ParseAllPossibleAtoms); |
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 |
|
726 // see: https://github.com/JamesHeinrich/getID3/issues/111 |
|
727 // Some corrupt files have been known to have high bits set in the number_entries field |
|
728 // This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000 |
|
729 // Workaround: mask off the upper byte and throw a warning if it's nonzero |
|
730 if ($atom_structure['number_entries'] > 0x000FFFFF) { |
|
731 if ($atom_structure['number_entries'] > 0x00FFFFFF) { |
|
732 $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Ignoring upper byte and interpreting this as 0x'.getid3_lib::PrintHexBytes(substr($atom_data, 5, 3), true, false).' = '.($atom_structure['number_entries'] & 0x00FFFFFF)); |
|
733 $atom_structure['number_entries'] = ($atom_structure['number_entries'] & 0x00FFFFFF); |
|
734 } else { |
626 } else { |
735 $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Please report this to info@getid3.org referencing bug report #111'); |
627 $this->warning('Error decompressing compressed MOV atom at offset '.$atom_structure['offset']); |
736 } |
628 } |
737 } |
629 break; |
738 |
630 |
739 $stsdEntriesDataOffset = 8; |
631 |
740 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
632 case 'dcom': // Data COMpression atom |
741 $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4)); |
633 $atom_structure['compression_id'] = $atom_data; |
742 $stsdEntriesDataOffset += 4; |
634 $atom_structure['compression_text'] = $this->QuicktimeDCOMLookup($atom_data); |
743 $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4); |
635 break; |
744 $stsdEntriesDataOffset += 4; |
636 |
745 $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6)); |
637 |
746 $stsdEntriesDataOffset += 6; |
638 case 'rdrf': // Reference movie Data ReFerence atom |
747 $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2)); |
639 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
748 $stsdEntriesDataOffset += 2; |
640 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
749 $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); |
641 $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001); |
750 $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); |
642 |
751 |
643 $atom_structure['reference_type_name'] = substr($atom_data, 4, 4); |
752 $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2)); |
644 $atom_structure['reference_length'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
753 $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2)); |
645 switch ($atom_structure['reference_type_name']) { |
754 $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4); |
646 case 'url ': |
755 |
647 $atom_structure['url'] = $this->NoNullString(substr($atom_data, 12)); |
756 switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) { |
|
757 |
|
758 case "\x00\x00\x00\x00": |
|
759 // audio tracks |
|
760 $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2)); |
|
761 $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2)); |
|
762 $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2)); |
|
763 $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2)); |
|
764 $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4)); |
|
765 |
|
766 // video tracks |
|
767 // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html |
|
768 $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); |
|
769 $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); |
|
770 $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); |
|
771 $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); |
|
772 $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); |
|
773 $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); |
|
774 $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4)); |
|
775 $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2)); |
|
776 $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4); |
|
777 $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2)); |
|
778 $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2)); |
|
779 |
|
780 switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
|
781 case '2vuY': |
|
782 case 'avc1': |
|
783 case 'cvid': |
|
784 case 'dvc ': |
|
785 case 'dvcp': |
|
786 case 'gif ': |
|
787 case 'h263': |
|
788 case 'jpeg': |
|
789 case 'kpcd': |
|
790 case 'mjpa': |
|
791 case 'mjpb': |
|
792 case 'mp4v': |
|
793 case 'png ': |
|
794 case 'raw ': |
|
795 case 'rle ': |
|
796 case 'rpza': |
|
797 case 'smc ': |
|
798 case 'SVQ1': |
|
799 case 'SVQ3': |
|
800 case 'tiff': |
|
801 case 'v210': |
|
802 case 'v216': |
|
803 case 'v308': |
|
804 case 'v408': |
|
805 case 'v410': |
|
806 case 'yuv2': |
|
807 $info['fileformat'] = 'mp4'; |
|
808 $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; |
|
809 // http://www.getid3.org/phpBB3/viewtopic.php?t=1550 |
|
810 //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 |
|
811 if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) { |
|
812 // assume that values stored here are more important than values stored in [tkhd] atom |
|
813 $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width']; |
|
814 $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height']; |
|
815 $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; |
|
816 $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; |
|
817 } |
|
818 break; |
|
819 |
|
820 case 'qtvr': |
|
821 $info['video']['dataformat'] = 'quicktimevr'; |
|
822 break; |
|
823 |
|
824 case 'mp4a': |
|
825 default: |
|
826 $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); |
|
827 $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate']; |
|
828 $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels']; |
|
829 $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth']; |
|
830 $info['audio']['codec'] = $info['quicktime']['audio']['codec']; |
|
831 $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate']; |
|
832 $info['audio']['channels'] = $info['quicktime']['audio']['channels']; |
|
833 $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth']; |
|
834 switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
|
835 case 'raw ': // PCM |
|
836 case 'alac': // Apple Lossless Audio Codec |
|
837 $info['audio']['lossless'] = true; |
|
838 break; |
|
839 default: |
|
840 $info['audio']['lossless'] = false; |
|
841 break; |
|
842 } |
|
843 break; |
|
844 } |
|
845 break; |
648 break; |
846 |
649 |
|
650 case 'alis': |
|
651 $atom_structure['file_alias'] = substr($atom_data, 12); |
|
652 break; |
|
653 |
|
654 case 'rsrc': |
|
655 $atom_structure['resource_alias'] = substr($atom_data, 12); |
|
656 break; |
|
657 |
847 default: |
658 default: |
848 switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
659 $atom_structure['data'] = substr($atom_data, 12); |
849 case 'mp4s': |
|
850 $info['fileformat'] = 'mp4'; |
|
851 break; |
|
852 |
|
853 default: |
|
854 // video atom |
|
855 $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); |
|
856 $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); |
|
857 $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); |
|
858 $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); |
|
859 $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4)); |
|
860 $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); |
|
861 $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); |
|
862 $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2)); |
|
863 $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1)); |
|
864 $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']); |
|
865 $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2)); |
|
866 $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2)); |
|
867 |
|
868 $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); |
|
869 $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']); |
|
870 |
|
871 if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { |
|
872 $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; |
|
873 $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); |
|
874 $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']); |
|
875 $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; |
|
876 $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; |
|
877 |
|
878 $info['video']['codec'] = $info['quicktime']['video']['codec']; |
|
879 $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth']; |
|
880 } |
|
881 $info['video']['lossless'] = false; |
|
882 $info['video']['pixel_aspect_ratio'] = (float) 1; |
|
883 break; |
|
884 } |
|
885 break; |
660 break; |
886 } |
661 } |
887 switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) { |
662 break; |
888 case 'mp4a': |
663 |
889 $info['audio']['dataformat'] = 'mp4'; |
664 |
890 $info['quicktime']['audio']['codec'] = 'mp4'; |
665 case 'rmqu': // Reference Movie QUality atom |
891 break; |
666 $atom_structure['movie_quality'] = getid3_lib::BigEndian2Int($atom_data); |
892 |
667 break; |
893 case '3ivx': |
668 |
894 case '3iv1': |
669 |
895 case '3iv2': |
670 case 'rmcs': // Reference Movie Cpu Speed atom |
896 $info['video']['dataformat'] = '3ivx'; |
671 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
897 break; |
672 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
898 |
673 $atom_structure['cpu_speed_rating'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
899 case 'xvid': |
674 break; |
900 $info['video']['dataformat'] = 'xvid'; |
675 |
901 break; |
676 |
902 |
677 case 'rmvc': // Reference Movie Version Check atom |
903 case 'mp4v': |
678 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
904 $info['video']['dataformat'] = 'mpeg4'; |
679 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
905 break; |
680 $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4); |
906 |
681 $atom_structure['gestalt_value_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
907 case 'divx': |
682 $atom_structure['gestalt_value'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
908 case 'div1': |
683 $atom_structure['gestalt_check_type'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); |
909 case 'div2': |
684 break; |
910 case 'div3': |
685 |
911 case 'div4': |
686 |
912 case 'div5': |
687 case 'rmcd': // Reference Movie Component check atom |
913 case 'div6': |
688 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
914 $info['video']['dataformat'] = 'divx'; |
689 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
915 break; |
690 $atom_structure['component_type'] = substr($atom_data, 4, 4); |
916 |
691 $atom_structure['component_subtype'] = substr($atom_data, 8, 4); |
917 default: |
692 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); |
918 // do nothing |
693 $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
919 break; |
694 $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); |
920 } |
695 $atom_structure['component_min_version'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 4)); |
921 unset($atom_structure['sample_description_table'][$i]['data']); |
696 break; |
922 } |
697 |
923 break; |
698 |
924 |
699 case 'rmdr': // Reference Movie Data Rate atom |
925 |
700 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
926 case 'stts': // Sample Table Time-to-Sample atom |
701 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
927 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
702 $atom_structure['data_rate'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
928 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
703 |
929 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
704 $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10; |
930 $sttsEntriesDataOffset = 8; |
705 break; |
931 //$FrameRateCalculatorArray = array(); |
706 |
932 $frames_count = 0; |
707 |
933 |
708 case 'rmla': // Reference Movie Language Atom |
934 $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']); |
709 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
935 if ($max_stts_entries_to_scan < $atom_structure['number_entries']) { |
710 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
936 $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).'); |
711 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
937 } |
712 |
938 for ($i = 0; $i < $max_stts_entries_to_scan; $i++) { |
713 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); |
939 $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); |
714 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { |
940 $sttsEntriesDataOffset += 4; |
715 $info['comments']['language'][] = $atom_structure['language']; |
941 $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); |
716 } |
942 $sttsEntriesDataOffset += 4; |
717 break; |
943 |
718 |
944 $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count']; |
719 |
945 |
720 case 'ptv ': // Print To Video - defines a movie's full screen mode |
946 // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM |
721 // http://developer.apple.com/documentation/QuickTime/APIREF/SOURCESIV/at_ptv-_pg.htm |
947 //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) { |
722 $atom_structure['display_size_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); |
948 // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration']; |
723 $atom_structure['reserved_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 2)); // hardcoded: 0x0000 |
949 // if ($stts_new_framerate <= 60) { |
724 $atom_structure['reserved_2'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x0000 |
950 // // some atoms have durations of "1" giving a very large framerate, which probably is not right |
725 $atom_structure['slide_show_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 1)); |
951 // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate); |
726 $atom_structure['play_on_open_flag'] = getid3_lib::BigEndian2Int(substr($atom_data, 7, 1)); |
952 // } |
727 |
953 //} |
728 $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag']; |
954 // |
729 $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag']; |
955 //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; |
730 |
956 } |
731 $ptv_lookup[0] = 'normal'; |
957 $info['quicktime']['stts_framecount'][] = $frames_count; |
732 $ptv_lookup[1] = 'double'; |
958 //$sttsFramesTotal = 0; |
733 $ptv_lookup[2] = 'half'; |
959 //$sttsSecondsTotal = 0; |
734 $ptv_lookup[3] = 'full'; |
960 //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { |
735 $ptv_lookup[4] = 'current'; |
961 // if (($frames_per_second > 60) || ($frames_per_second < 1)) { |
736 if (isset($ptv_lookup[$atom_structure['display_size_raw']])) { |
962 // // not video FPS information, probably audio information |
737 $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']]; |
963 // $sttsFramesTotal = 0; |
738 } else { |
964 // $sttsSecondsTotal = 0; |
739 $this->warning('unknown "ptv " display constant ('.$atom_structure['display_size_raw'].')'); |
965 // break; |
740 } |
966 // } |
741 break; |
967 // $sttsFramesTotal += $frame_count; |
742 |
968 // $sttsSecondsTotal += $frame_count / $frames_per_second; |
743 |
969 //} |
744 case 'stsd': // Sample Table Sample Description atom |
970 //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { |
|
971 // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) { |
|
972 // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; |
|
973 // } |
|
974 //} |
|
975 break; |
|
976 |
|
977 |
|
978 case 'stss': // Sample Table Sync Sample (key frames) atom |
|
979 if ($ParseAllPossibleAtoms) { |
|
980 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
745 $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 |
746 $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)); |
747 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
983 $stssEntriesDataOffset = 8; |
748 |
|
749 // see: https://github.com/JamesHeinrich/getID3/issues/111 |
|
750 // Some corrupt files have been known to have high bits set in the number_entries field |
|
751 // This field shouldn't really need to be 32-bits, values stores are likely in the range 1-100000 |
|
752 // Workaround: mask off the upper byte and throw a warning if it's nonzero |
|
753 if ($atom_structure['number_entries'] > 0x000FFFFF) { |
|
754 if ($atom_structure['number_entries'] > 0x00FFFFFF) { |
|
755 $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Ignoring upper byte and interpreting this as 0x'.getid3_lib::PrintHexBytes(substr($atom_data, 5, 3), true, false).' = '.($atom_structure['number_entries'] & 0x00FFFFFF)); |
|
756 $atom_structure['number_entries'] = ($atom_structure['number_entries'] & 0x00FFFFFF); |
|
757 } else { |
|
758 $this->warning('"stsd" atom contains improbably large number_entries (0x'.getid3_lib::PrintHexBytes(substr($atom_data, 4, 4), true, false).' = '.$atom_structure['number_entries'].'), probably in error. Please report this to info@getid3.org referencing bug report #111'); |
|
759 } |
|
760 } |
|
761 |
|
762 $stsdEntriesDataOffset = 8; |
984 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
763 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
985 $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4)); |
764 $atom_structure['sample_description_table'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4)); |
986 $stssEntriesDataOffset += 4; |
765 $stsdEntriesDataOffset += 4; |
987 } |
766 $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4); |
988 } |
767 $stsdEntriesDataOffset += 4; |
989 break; |
768 $atom_structure['sample_description_table'][$i]['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6)); |
990 |
769 $stsdEntriesDataOffset += 6; |
991 |
770 $atom_structure['sample_description_table'][$i]['reference_index'] = getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2)); |
992 case 'stsc': // Sample Table Sample-to-Chunk atom |
771 $stsdEntriesDataOffset += 2; |
993 if ($ParseAllPossibleAtoms) { |
772 $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2)); |
|
773 $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2); |
|
774 |
|
775 if (substr($atom_structure['sample_description_table'][$i]['data'], 1, 54) == 'application/octet-stream;type=com.parrot.videometadata') { |
|
776 // special handling for apparently-malformed (TextMetaDataSampleEntry?) data for some version of Parrot drones |
|
777 $atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['mime_type'] = substr($atom_structure['sample_description_table'][$i]['data'], 1, 55); |
|
778 $atom_structure['sample_description_table'][$i]['parrot_frame_metadata']['metadata_version'] = (int) substr($atom_structure['sample_description_table'][$i]['data'], 55, 1); |
|
779 unset($atom_structure['sample_description_table'][$i]['data']); |
|
780 $this->warning('incomplete/incorrect handling of "stsd" with Parrot metadata in this version of getID3() ['.$this->getid3->version().']'); |
|
781 continue; |
|
782 } |
|
783 |
|
784 $atom_structure['sample_description_table'][$i]['encoder_version'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 0, 2)); |
|
785 $atom_structure['sample_description_table'][$i]['encoder_revision'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 2, 2)); |
|
786 $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4); |
|
787 |
|
788 switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) { |
|
789 |
|
790 case "\x00\x00\x00\x00": |
|
791 // audio tracks |
|
792 $atom_structure['sample_description_table'][$i]['audio_channels'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 2)); |
|
793 $atom_structure['sample_description_table'][$i]['audio_bit_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 10, 2)); |
|
794 $atom_structure['sample_description_table'][$i]['audio_compression_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 2)); |
|
795 $atom_structure['sample_description_table'][$i]['audio_packet_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 14, 2)); |
|
796 $atom_structure['sample_description_table'][$i]['audio_sample_rate'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 16, 4)); |
|
797 |
|
798 // video tracks |
|
799 // http://developer.apple.com/library/mac/#documentation/QuickTime/QTFF/QTFFChap3/qtff3.html |
|
800 $atom_structure['sample_description_table'][$i]['temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); |
|
801 $atom_structure['sample_description_table'][$i]['spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); |
|
802 $atom_structure['sample_description_table'][$i]['width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); |
|
803 $atom_structure['sample_description_table'][$i]['height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); |
|
804 $atom_structure['sample_description_table'][$i]['resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); |
|
805 $atom_structure['sample_description_table'][$i]['resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); |
|
806 $atom_structure['sample_description_table'][$i]['data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 4)); |
|
807 $atom_structure['sample_description_table'][$i]['frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 36, 2)); |
|
808 $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4); |
|
809 $atom_structure['sample_description_table'][$i]['pixel_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 42, 2)); |
|
810 $atom_structure['sample_description_table'][$i]['color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 44, 2)); |
|
811 |
|
812 switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
|
813 case '2vuY': |
|
814 case 'avc1': |
|
815 case 'cvid': |
|
816 case 'dvc ': |
|
817 case 'dvcp': |
|
818 case 'gif ': |
|
819 case 'h263': |
|
820 case 'jpeg': |
|
821 case 'kpcd': |
|
822 case 'mjpa': |
|
823 case 'mjpb': |
|
824 case 'mp4v': |
|
825 case 'png ': |
|
826 case 'raw ': |
|
827 case 'rle ': |
|
828 case 'rpza': |
|
829 case 'smc ': |
|
830 case 'SVQ1': |
|
831 case 'SVQ3': |
|
832 case 'tiff': |
|
833 case 'v210': |
|
834 case 'v216': |
|
835 case 'v308': |
|
836 case 'v408': |
|
837 case 'v410': |
|
838 case 'yuv2': |
|
839 $info['fileformat'] = 'mp4'; |
|
840 $info['video']['fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; |
|
841 if ($this->QuicktimeVideoCodecLookup($info['video']['fourcc'])) { |
|
842 $info['video']['fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($info['video']['fourcc']); |
|
843 } |
|
844 |
|
845 // https://www.getid3.org/phpBB3/viewtopic.php?t=1550 |
|
846 //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 |
|
847 if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) { |
|
848 // assume that values stored here are more important than values stored in [tkhd] atom |
|
849 $info['video']['resolution_x'] = $atom_structure['sample_description_table'][$i]['width']; |
|
850 $info['video']['resolution_y'] = $atom_structure['sample_description_table'][$i]['height']; |
|
851 $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; |
|
852 $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; |
|
853 } |
|
854 break; |
|
855 |
|
856 case 'qtvr': |
|
857 $info['video']['dataformat'] = 'quicktimevr'; |
|
858 break; |
|
859 |
|
860 case 'mp4a': |
|
861 default: |
|
862 $info['quicktime']['audio']['codec'] = $this->QuicktimeAudioCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); |
|
863 $info['quicktime']['audio']['sample_rate'] = $atom_structure['sample_description_table'][$i]['audio_sample_rate']; |
|
864 $info['quicktime']['audio']['channels'] = $atom_structure['sample_description_table'][$i]['audio_channels']; |
|
865 $info['quicktime']['audio']['bit_depth'] = $atom_structure['sample_description_table'][$i]['audio_bit_depth']; |
|
866 $info['audio']['codec'] = $info['quicktime']['audio']['codec']; |
|
867 $info['audio']['sample_rate'] = $info['quicktime']['audio']['sample_rate']; |
|
868 $info['audio']['channels'] = $info['quicktime']['audio']['channels']; |
|
869 $info['audio']['bits_per_sample'] = $info['quicktime']['audio']['bit_depth']; |
|
870 switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
|
871 case 'raw ': // PCM |
|
872 case 'alac': // Apple Lossless Audio Codec |
|
873 case 'sowt': // signed/two's complement (Little Endian) |
|
874 case 'twos': // signed/two's complement (Big Endian) |
|
875 case 'in24': // 24-bit Integer |
|
876 case 'in32': // 32-bit Integer |
|
877 case 'fl32': // 32-bit Floating Point |
|
878 case 'fl64': // 64-bit Floating Point |
|
879 $info['audio']['lossless'] = $info['quicktime']['audio']['lossless'] = true; |
|
880 $info['audio']['bitrate'] = $info['quicktime']['audio']['bitrate'] = $info['audio']['channels'] * $info['audio']['bits_per_sample'] * $info['audio']['sample_rate']; |
|
881 break; |
|
882 default: |
|
883 $info['audio']['lossless'] = false; |
|
884 break; |
|
885 } |
|
886 break; |
|
887 } |
|
888 break; |
|
889 |
|
890 default: |
|
891 switch ($atom_structure['sample_description_table'][$i]['data_format']) { |
|
892 case 'mp4s': |
|
893 $info['fileformat'] = 'mp4'; |
|
894 break; |
|
895 |
|
896 default: |
|
897 // video atom |
|
898 $atom_structure['sample_description_table'][$i]['video_temporal_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 8, 4)); |
|
899 $atom_structure['sample_description_table'][$i]['video_spatial_quality'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 12, 4)); |
|
900 $atom_structure['sample_description_table'][$i]['video_frame_width'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 16, 2)); |
|
901 $atom_structure['sample_description_table'][$i]['video_frame_height'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 18, 2)); |
|
902 $atom_structure['sample_description_table'][$i]['video_resolution_x'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 20, 4)); |
|
903 $atom_structure['sample_description_table'][$i]['video_resolution_y'] = getid3_lib::FixedPoint16_16(substr($atom_structure['sample_description_table'][$i]['data'], 24, 4)); |
|
904 $atom_structure['sample_description_table'][$i]['video_data_size'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 28, 4)); |
|
905 $atom_structure['sample_description_table'][$i]['video_frame_count'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 32, 2)); |
|
906 $atom_structure['sample_description_table'][$i]['video_encoder_name_len'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 34, 1)); |
|
907 $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']); |
|
908 $atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 66, 2)); |
|
909 $atom_structure['sample_description_table'][$i]['video_color_table_id'] = getid3_lib::BigEndian2Int(substr($atom_structure['sample_description_table'][$i]['data'], 68, 2)); |
|
910 |
|
911 $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color'); |
|
912 $atom_structure['sample_description_table'][$i]['video_pixel_color_name'] = $this->QuicktimeColorNameLookup($atom_structure['sample_description_table'][$i]['video_pixel_color_depth']); |
|
913 |
|
914 if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') { |
|
915 $info['quicktime']['video']['codec_fourcc'] = $atom_structure['sample_description_table'][$i]['data_format']; |
|
916 $info['quicktime']['video']['codec_fourcc_lookup'] = $this->QuicktimeVideoCodecLookup($atom_structure['sample_description_table'][$i]['data_format']); |
|
917 $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']); |
|
918 $info['quicktime']['video']['color_depth'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_depth']; |
|
919 $info['quicktime']['video']['color_depth_name'] = $atom_structure['sample_description_table'][$i]['video_pixel_color_name']; |
|
920 |
|
921 $info['video']['codec'] = $info['quicktime']['video']['codec']; |
|
922 $info['video']['bits_per_sample'] = $info['quicktime']['video']['color_depth']; |
|
923 } |
|
924 $info['video']['lossless'] = false; |
|
925 $info['video']['pixel_aspect_ratio'] = (float) 1; |
|
926 break; |
|
927 } |
|
928 break; |
|
929 } |
|
930 switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) { |
|
931 case 'mp4a': |
|
932 $info['audio']['dataformat'] = 'mp4'; |
|
933 $info['quicktime']['audio']['codec'] = 'mp4'; |
|
934 break; |
|
935 |
|
936 case '3ivx': |
|
937 case '3iv1': |
|
938 case '3iv2': |
|
939 $info['video']['dataformat'] = '3ivx'; |
|
940 break; |
|
941 |
|
942 case 'xvid': |
|
943 $info['video']['dataformat'] = 'xvid'; |
|
944 break; |
|
945 |
|
946 case 'mp4v': |
|
947 $info['video']['dataformat'] = 'mpeg4'; |
|
948 break; |
|
949 |
|
950 case 'divx': |
|
951 case 'div1': |
|
952 case 'div2': |
|
953 case 'div3': |
|
954 case 'div4': |
|
955 case 'div5': |
|
956 case 'div6': |
|
957 $info['video']['dataformat'] = 'divx'; |
|
958 break; |
|
959 |
|
960 default: |
|
961 // do nothing |
|
962 break; |
|
963 } |
|
964 unset($atom_structure['sample_description_table'][$i]['data']); |
|
965 } |
|
966 break; |
|
967 |
|
968 |
|
969 case 'stts': // Sample Table Time-to-Sample atom |
994 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
970 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
995 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
971 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
996 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
972 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
997 $stscEntriesDataOffset = 8; |
973 $sttsEntriesDataOffset = 8; |
998 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
974 //$FrameRateCalculatorArray = array(); |
999 $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); |
975 $frames_count = 0; |
1000 $stscEntriesDataOffset += 4; |
976 |
1001 $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); |
977 $max_stts_entries_to_scan = ($info['php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure['number_entries']) : $atom_structure['number_entries']); |
1002 $stscEntriesDataOffset += 4; |
978 if ($max_stts_entries_to_scan < $atom_structure['number_entries']) { |
1003 $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); |
979 $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($this->getid3->memory_limit / 1048576).'MB).'); |
1004 $stscEntriesDataOffset += 4; |
980 } |
1005 } |
981 for ($i = 0; $i < $max_stts_entries_to_scan; $i++) { |
1006 } |
982 $atom_structure['time_to_sample_table'][$i]['sample_count'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); |
1007 break; |
983 $sttsEntriesDataOffset += 4; |
1008 |
984 $atom_structure['time_to_sample_table'][$i]['sample_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4)); |
1009 |
985 $sttsEntriesDataOffset += 4; |
1010 case 'stsz': // Sample Table SiZe atom |
986 |
1011 if ($ParseAllPossibleAtoms) { |
987 $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count']; |
1012 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
988 |
1013 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
989 // THIS SECTION REPLACED WITH CODE IN "stbl" ATOM |
1014 $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
990 //if (!empty($info['quicktime']['time_scale']) && ($atom_structure['time_to_sample_table'][$i]['sample_duration'] > 0)) { |
1015 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
991 // $stts_new_framerate = $info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration']; |
1016 $stszEntriesDataOffset = 12; |
992 // if ($stts_new_framerate <= 60) { |
1017 if ($atom_structure['sample_size'] == 0) { |
993 // // some atoms have durations of "1" giving a very large framerate, which probably is not right |
|
994 // $info['video']['frame_rate'] = max($info['video']['frame_rate'], $stts_new_framerate); |
|
995 // } |
|
996 //} |
|
997 // |
|
998 //$FrameRateCalculatorArray[($info['quicktime']['time_scale'] / $atom_structure['time_to_sample_table'][$i]['sample_duration'])] += $atom_structure['time_to_sample_table'][$i]['sample_count']; |
|
999 } |
|
1000 $info['quicktime']['stts_framecount'][] = $frames_count; |
|
1001 //$sttsFramesTotal = 0; |
|
1002 //$sttsSecondsTotal = 0; |
|
1003 //foreach ($FrameRateCalculatorArray as $frames_per_second => $frame_count) { |
|
1004 // if (($frames_per_second > 60) || ($frames_per_second < 1)) { |
|
1005 // // not video FPS information, probably audio information |
|
1006 // $sttsFramesTotal = 0; |
|
1007 // $sttsSecondsTotal = 0; |
|
1008 // break; |
|
1009 // } |
|
1010 // $sttsFramesTotal += $frame_count; |
|
1011 // $sttsSecondsTotal += $frame_count / $frames_per_second; |
|
1012 //} |
|
1013 //if (($sttsFramesTotal > 0) && ($sttsSecondsTotal > 0)) { |
|
1014 // if (($sttsFramesTotal / $sttsSecondsTotal) > $info['video']['frame_rate']) { |
|
1015 // $info['video']['frame_rate'] = $sttsFramesTotal / $sttsSecondsTotal; |
|
1016 // } |
|
1017 //} |
|
1018 break; |
|
1019 |
|
1020 |
|
1021 case 'stss': // Sample Table Sync Sample (key frames) atom |
|
1022 if ($ParseAllPossibleAtoms) { |
|
1023 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1024 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1025 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1026 $stssEntriesDataOffset = 8; |
1018 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
1027 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
1019 $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4)); |
1028 $atom_structure['time_to_sample_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4)); |
1020 $stszEntriesDataOffset += 4; |
1029 $stssEntriesDataOffset += 4; |
1021 } |
1030 } |
1022 } |
1031 } |
1023 } |
1032 break; |
1024 break; |
1033 |
1025 |
1034 |
1026 |
1035 case 'stsc': // Sample Table Sample-to-Chunk atom |
1027 case 'stco': // Sample Table Chunk Offset atom |
1036 if ($ParseAllPossibleAtoms) { |
1028 if ($ParseAllPossibleAtoms) { |
1037 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1038 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1039 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1040 $stscEntriesDataOffset = 8; |
|
1041 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
|
1042 $atom_structure['sample_to_chunk_table'][$i]['first_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); |
|
1043 $stscEntriesDataOffset += 4; |
|
1044 $atom_structure['sample_to_chunk_table'][$i]['samples_per_chunk'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); |
|
1045 $stscEntriesDataOffset += 4; |
|
1046 $atom_structure['sample_to_chunk_table'][$i]['sample_description'] = getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4)); |
|
1047 $stscEntriesDataOffset += 4; |
|
1048 } |
|
1049 } |
|
1050 break; |
|
1051 |
|
1052 |
|
1053 case 'stsz': // Sample Table SiZe atom |
|
1054 if ($ParseAllPossibleAtoms) { |
|
1055 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1056 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1057 $atom_structure['sample_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1058 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
|
1059 $stszEntriesDataOffset = 12; |
|
1060 if ($atom_structure['sample_size'] == 0) { |
|
1061 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
|
1062 $atom_structure['sample_size_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4)); |
|
1063 $stszEntriesDataOffset += 4; |
|
1064 } |
|
1065 } |
|
1066 } |
|
1067 break; |
|
1068 |
|
1069 |
|
1070 case 'stco': // Sample Table Chunk Offset atom |
|
1071 // if (true) { |
|
1072 if ($ParseAllPossibleAtoms) { |
|
1073 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1074 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1075 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1076 $stcoEntriesDataOffset = 8; |
|
1077 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
|
1078 $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4)); |
|
1079 $stcoEntriesDataOffset += 4; |
|
1080 } |
|
1081 } |
|
1082 break; |
|
1083 |
|
1084 |
|
1085 case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files) |
|
1086 if ($ParseAllPossibleAtoms) { |
|
1087 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1088 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1089 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1090 $stcoEntriesDataOffset = 8; |
|
1091 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
|
1092 $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8)); |
|
1093 $stcoEntriesDataOffset += 8; |
|
1094 } |
|
1095 } |
|
1096 break; |
|
1097 |
|
1098 |
|
1099 case 'dref': // Data REFerence atom |
1029 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1100 $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 |
1101 $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)); |
1102 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1032 $stcoEntriesDataOffset = 8; |
1103 $drefDataOffset = 8; |
1033 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
1104 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
1034 $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4)); |
1105 $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4)); |
1035 $stcoEntriesDataOffset += 4; |
1106 $drefDataOffset += 4; |
1036 } |
1107 $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4); |
1037 } |
1108 $drefDataOffset += 4; |
1038 break; |
1109 $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1)); |
1039 |
1110 $drefDataOffset += 1; |
1040 |
1111 $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000 |
1041 case 'co64': // Chunk Offset 64-bit (version of "stco" that supports > 2GB files) |
1112 $drefDataOffset += 3; |
1042 if ($ParseAllPossibleAtoms) { |
1113 $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); |
|
1114 $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); |
|
1115 |
|
1116 $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001); |
|
1117 } |
|
1118 break; |
|
1119 |
|
1120 |
|
1121 case 'gmin': // base Media INformation atom |
|
1122 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1123 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1124 $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
|
1125 $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); |
|
1126 $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); |
|
1127 $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); |
|
1128 $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2)); |
|
1129 $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); |
|
1130 break; |
|
1131 |
|
1132 |
|
1133 case 'smhd': // Sound Media information HeaDer atom |
|
1134 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1135 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1136 $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
|
1137 $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); |
|
1138 break; |
|
1139 |
|
1140 |
|
1141 case 'vmhd': // Video Media information HeaDer atom |
|
1142 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1143 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
|
1144 $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
|
1145 $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); |
|
1146 $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); |
|
1147 $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); |
|
1148 |
|
1149 $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001); |
|
1150 break; |
|
1151 |
|
1152 |
|
1153 case 'hdlr': // HanDLeR reference atom |
|
1154 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1155 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1156 $atom_structure['component_type'] = substr($atom_data, 4, 4); |
|
1157 $atom_structure['component_subtype'] = substr($atom_data, 8, 4); |
|
1158 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); |
|
1159 $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
|
1160 $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); |
|
1161 $atom_structure['component_name'] = $this->MaybePascal2String(substr($atom_data, 24)); |
|
1162 |
|
1163 if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) { |
|
1164 $info['video']['dataformat'] = 'quicktimevr'; |
|
1165 } |
|
1166 break; |
|
1167 |
|
1168 |
|
1169 case 'mdhd': // MeDia HeaDer atom |
|
1170 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1171 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1172 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1173 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
|
1174 $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
|
1175 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
|
1176 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2)); |
|
1177 $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2)); |
|
1178 |
|
1179 if ($atom_structure['time_scale'] == 0) { |
|
1180 $this->error('Corrupt Quicktime file: mdhd.time_scale == zero'); |
|
1181 return false; |
|
1182 } |
|
1183 $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']); |
|
1184 |
|
1185 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); |
|
1186 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); |
|
1187 $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; |
|
1188 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); |
|
1189 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { |
|
1190 $info['comments']['language'][] = $atom_structure['language']; |
|
1191 } |
|
1192 $info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix']; |
|
1193 $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix']; |
|
1194 break; |
|
1195 |
|
1196 |
|
1197 case 'pnot': // Preview atom |
|
1198 $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format" |
|
1199 $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00 |
|
1200 $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT' |
|
1201 $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01 |
|
1202 |
|
1203 $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']); |
|
1204 $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modification_date_unix']; |
|
1205 break; |
|
1206 |
|
1207 |
|
1208 case 'crgn': // Clipping ReGioN atom |
|
1209 $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box, |
|
1210 $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields |
|
1211 $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. |
|
1212 break; |
|
1213 |
|
1214 |
|
1215 case 'load': // track LOAD settings atom |
|
1216 $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
|
1217 $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1218 $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
|
1219 $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
|
1220 |
|
1221 $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020); |
|
1222 $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100); |
|
1223 break; |
|
1224 |
|
1225 |
|
1226 case 'tmcd': // TiMe CoDe atom |
|
1227 case 'chap': // CHAPter list atom |
|
1228 case 'sync': // SYNChronization atom |
|
1229 case 'scpt': // tranSCriPT atom |
|
1230 case 'ssrc': // non-primary SouRCe atom |
|
1231 for ($i = 0; $i < strlen($atom_data); $i += 4) { |
|
1232 @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); |
|
1233 } |
|
1234 break; |
|
1235 |
|
1236 |
|
1237 case 'elst': // Edit LiST atom |
1043 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1238 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1044 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1239 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1045 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1240 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1046 $stcoEntriesDataOffset = 8; |
1241 for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) { |
1047 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
1242 $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4)); |
1048 $atom_structure['chunk_offset_table'][$i] = getid3_lib::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8)); |
1243 $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4)); |
1049 $stcoEntriesDataOffset += 8; |
1244 $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4)); |
1050 } |
1245 } |
1051 } |
1246 break; |
1052 break; |
1247 |
1053 |
1248 |
1054 |
1249 case 'kmat': // compressed MATte atom |
1055 case 'dref': // Data REFerence atom |
1250 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1056 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1251 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1057 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1252 $atom_structure['matte_data_raw'] = substr($atom_data, 4); |
1058 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1253 break; |
1059 $drefDataOffset = 8; |
1254 |
1060 for ($i = 0; $i < $atom_structure['number_entries']; $i++) { |
1255 |
1061 $atom_structure['data_references'][$i]['size'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 4)); |
1256 case 'ctab': // Color TABle atom |
1062 $drefDataOffset += 4; |
1257 $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000 |
1063 $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4); |
1258 $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000 |
1064 $drefDataOffset += 4; |
1259 $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1; |
1065 $atom_structure['data_references'][$i]['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 1)); |
1260 for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) { |
1066 $drefDataOffset += 1; |
1261 $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2)); |
1067 $atom_structure['data_references'][$i]['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $drefDataOffset, 3)); // hardcoded: 0x0000 |
1262 $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2)); |
1068 $drefDataOffset += 3; |
1263 $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2)); |
1069 $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3)); |
1264 $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2)); |
1070 $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3); |
1265 } |
1071 |
1266 break; |
1072 $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001); |
1267 |
1073 } |
1268 |
1074 break; |
1269 case 'mvhd': // MoVie HeaDer atom |
1075 |
1270 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1076 |
1271 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
1077 case 'gmin': // base Media INformation atom |
1272 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1078 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1273 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
1079 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1274 $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
1080 $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
1275 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
1081 $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); |
1276 $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4)); |
1082 $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); |
1277 $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2)); |
1083 $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); |
1278 $atom_structure['reserved'] = substr($atom_data, 26, 10); |
1084 $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 2)); |
1279 $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4)); |
1085 $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 14, 2)); |
1280 $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); |
1086 break; |
1281 $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4)); |
1087 |
1282 $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4)); |
1088 |
1283 $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); |
1089 case 'smhd': // Sound Media information HeaDer atom |
1284 $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4)); |
1090 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1285 $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4)); |
1091 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1286 $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); |
1092 $atom_structure['balance'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
1287 $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4)); |
1093 $atom_structure['reserved'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); |
1288 $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4)); |
1094 break; |
1289 $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4)); |
1095 |
1290 $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4)); |
1096 |
1291 $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4)); |
1097 case 'vmhd': // Video Media information HeaDer atom |
1292 $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4)); |
1098 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1293 $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4)); |
1099 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
1294 $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4)); |
1100 $atom_structure['graphics_mode'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
1295 |
1101 $atom_structure['opcolor_red'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)); |
1296 if ($atom_structure['time_scale'] == 0) { |
1102 $atom_structure['opcolor_green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 2)); |
1297 $this->error('Corrupt Quicktime file: mvhd.time_scale == zero'); |
1103 $atom_structure['opcolor_blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); |
1298 return false; |
1104 |
1299 } |
1105 $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001); |
1300 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); |
1106 break; |
1301 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); |
1107 |
1302 $info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix']; |
1108 |
1303 $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix']; |
1109 case 'hdlr': // HanDLeR reference atom |
1304 $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']); |
1110 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1305 $info['quicktime']['display_scale'] = $atom_structure['matrix_a']; |
1111 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1306 $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; |
1112 $atom_structure['component_type'] = substr($atom_data, 4, 4); |
1307 break; |
1113 $atom_structure['component_subtype'] = substr($atom_data, 8, 4); |
1308 |
1114 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4); |
1309 |
1115 $atom_structure['component_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
1310 case 'tkhd': // TracK HeaDer atom |
1116 $atom_structure['component_flags_mask'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); |
1311 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1117 $atom_structure['component_name'] = $this->Pascal2String(substr($atom_data, 24)); |
1312 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
1118 |
1313 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1119 if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) { |
1314 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
1120 $info['video']['dataformat'] = 'quicktimevr'; |
1315 $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
1121 } |
1316 $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
1122 break; |
1317 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); |
1123 |
1318 $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8)); |
1124 |
1319 $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2)); |
1125 case 'mdhd': // MeDia HeaDer atom |
1320 $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2)); |
1126 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1321 $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2)); |
1127 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1322 $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2)); |
1128 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1323 // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html |
1129 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
1324 // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737 |
1130 $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
1325 $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); |
1131 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
1326 $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4)); |
1132 $atom_structure['language_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 2)); |
1327 $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4)); |
1133 $atom_structure['quality'] = getid3_lib::BigEndian2Int(substr($atom_data, 22, 2)); |
1328 $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); |
1134 |
1329 $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4)); |
1135 if ($atom_structure['time_scale'] == 0) { |
1330 $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4)); |
1136 $this->error('Corrupt Quicktime file: mdhd.time_scale == zero'); |
1331 $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); |
1137 return false; |
1332 $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4)); |
1138 } |
1333 $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4)); |
1139 $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']); |
1334 $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4)); |
1140 |
1335 $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4)); |
1141 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); |
1336 $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001); |
1142 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); |
1337 $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002); |
1143 $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; |
1338 $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004); |
1144 $atom_structure['language'] = $this->QuicktimeLanguageLookup($atom_structure['language_id']); |
1339 $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008); |
1145 if (empty($info['comments']['language']) || (!in_array($atom_structure['language'], $info['comments']['language']))) { |
1340 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); |
1146 $info['comments']['language'][] = $atom_structure['language']; |
1341 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); |
1147 } |
1342 $info['quicktime']['timestamps_unix']['create'][$atom_structure['hierarchy']] = $atom_structure['creation_time_unix']; |
1148 break; |
1343 $info['quicktime']['timestamps_unix']['modify'][$atom_structure['hierarchy']] = $atom_structure['modify_time_unix']; |
1149 |
1344 |
1150 |
1345 // https://www.getid3.org/phpBB3/viewtopic.php?t=1908 |
1151 case 'pnot': // Preview atom |
1346 // attempt to compute rotation from matrix values |
1152 $atom_structure['modification_date'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // "standard Macintosh format" |
1347 // 2017-Dec-28: uncertain if 90/270 are correctly oriented; values returned by FixedPoint16_16 should perhaps be -1 instead of 65535(?) |
1153 $atom_structure['version_number'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x00 |
1348 $matrixRotation = 0; |
1154 $atom_structure['atom_type'] = substr($atom_data, 6, 4); // usually: 'PICT' |
1349 switch ($atom_structure['matrix_a'].':'.$atom_structure['matrix_b'].':'.$atom_structure['matrix_c'].':'.$atom_structure['matrix_d']) { |
1155 $atom_structure['atom_index'] = getid3_lib::BigEndian2Int(substr($atom_data, 10, 2)); // usually: 0x01 |
1350 case '1:0:0:1': $matrixRotation = 0; break; |
1156 |
1351 case '0:1:65535:0': $matrixRotation = 90; break; |
1157 $atom_structure['modification_date_unix'] = getid3_lib::DateMac2Unix($atom_structure['modification_date']); |
1352 case '65535:0:0:65535': $matrixRotation = 180; break; |
1158 break; |
1353 case '0:65535:1:0': $matrixRotation = 270; break; |
1159 |
1354 default: break; |
1160 |
1355 } |
1161 case 'crgn': // Clipping ReGioN atom |
1356 |
1162 $atom_structure['region_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 2)); // The Region size, Region boundary box, |
1357 // https://www.getid3.org/phpBB3/viewtopic.php?t=2468 |
1163 $atom_structure['boundary_box'] = getid3_lib::BigEndian2Int(substr($atom_data, 2, 8)); // and Clipping region data fields |
1358 // The rotation matrix can appear in the Quicktime file multiple times, at least once for each track, |
1164 $atom_structure['clipping_data'] = substr($atom_data, 10); // constitute a QuickDraw region. |
1359 // and it's possible that only the video track (or, in theory, one of the video tracks) is flagged as |
1165 break; |
1360 // rotated while the other tracks (e.g. audio) is tagged as rotation=0 (behavior noted on iPhone 8 Plus) |
1166 |
1361 // The correct solution would be to check if the TrackID associated with the rotation matrix is indeed |
1167 |
1362 // a video track (or the main video track) and only set the rotation then, but since information about |
1168 case 'load': // track LOAD settings atom |
1363 // what track is what is not trivially there to be examined, the lazy solution is to set the rotation |
1169 $atom_structure['preload_start_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
1364 // if it is found to be nonzero, on the assumption that tracks that don't need it will have rotation set |
1170 $atom_structure['preload_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1365 // to zero (and be effectively ignored) and the video track will have rotation set correctly, which will |
1171 $atom_structure['preload_flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
1366 // either be zero and automatically correct, or nonzero and be set correctly. |
1172 $atom_structure['default_hints_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
1367 if (!isset($info['video']['rotate']) || (($info['video']['rotate'] == 0) && ($matrixRotation > 0))) { |
1173 |
1368 $info['quicktime']['video']['rotate'] = $info['video']['rotate'] = $matrixRotation; |
1174 $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020); |
1369 } |
1175 $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100); |
1370 |
1176 break; |
1371 if ($atom_structure['flags']['enabled'] == 1) { |
1177 |
1372 if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) { |
1178 |
1373 $info['video']['resolution_x'] = $atom_structure['width']; |
1179 case 'tmcd': // TiMe CoDe atom |
1374 $info['video']['resolution_y'] = $atom_structure['height']; |
1180 case 'chap': // CHAPter list atom |
1375 } |
1181 case 'sync': // SYNChronization atom |
1376 $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']); |
1182 case 'scpt': // tranSCriPT atom |
1377 $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']); |
1183 case 'ssrc': // non-primary SouRCe atom |
1378 $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; |
1184 for ($i = 0; $i < strlen($atom_data); $i += 4) { |
1379 $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; |
1185 @$atom_structure['track_id'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); |
1380 } else { |
1186 } |
1381 // see: https://www.getid3.org/phpBB3/viewtopic.php?t=1295 |
1187 break; |
1382 //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); } |
1188 |
1383 //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); } |
1189 |
1384 //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); } |
1190 case 'elst': // Edit LiST atom |
1385 } |
1191 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
1386 break; |
1192 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
1387 |
1193 $atom_structure['number_entries'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1388 |
1194 for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) { |
1389 case 'iods': // Initial Object DeScriptor atom |
1195 $atom_structure['edit_list'][$i]['track_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4)); |
1390 // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h |
1196 $atom_structure['edit_list'][$i]['media_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4)); |
1391 // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html |
1197 $atom_structure['edit_list'][$i]['media_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4)); |
1392 $offset = 0; |
1198 } |
1393 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
1199 break; |
|
1200 |
|
1201 |
|
1202 case 'kmat': // compressed MATte atom |
|
1203 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1204 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); // hardcoded: 0x0000 |
|
1205 $atom_structure['matte_data_raw'] = substr($atom_data, 4); |
|
1206 break; |
|
1207 |
|
1208 |
|
1209 case 'ctab': // Color TABle atom |
|
1210 $atom_structure['color_table_seed'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); // hardcoded: 0x00000000 |
|
1211 $atom_structure['color_table_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); // hardcoded: 0x8000 |
|
1212 $atom_structure['color_table_size'] = getid3_lib::BigEndian2Int(substr($atom_data, 6, 2)) + 1; |
|
1213 for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) { |
|
1214 $atom_structure['color_table'][$colortableentry]['alpha'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2)); |
|
1215 $atom_structure['color_table'][$colortableentry]['red'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2)); |
|
1216 $atom_structure['color_table'][$colortableentry]['green'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2)); |
|
1217 $atom_structure['color_table'][$colortableentry]['blue'] = getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2)); |
|
1218 } |
|
1219 break; |
|
1220 |
|
1221 |
|
1222 case 'mvhd': // MoVie HeaDer atom |
|
1223 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1224 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
|
1225 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1226 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
|
1227 $atom_structure['time_scale'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
|
1228 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
|
1229 $atom_structure['preferred_rate'] = getid3_lib::FixedPoint16_16(substr($atom_data, 20, 4)); |
|
1230 $atom_structure['preferred_volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 24, 2)); |
|
1231 $atom_structure['reserved'] = substr($atom_data, 26, 10); |
|
1232 $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 36, 4)); |
|
1233 $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); |
|
1234 $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 44, 4)); |
|
1235 $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 48, 4)); |
|
1236 $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); |
|
1237 $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 56, 4)); |
|
1238 $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 60, 4)); |
|
1239 $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); |
|
1240 $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 68, 4)); |
|
1241 $atom_structure['preview_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 72, 4)); |
|
1242 $atom_structure['preview_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 76, 4)); |
|
1243 $atom_structure['poster_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 80, 4)); |
|
1244 $atom_structure['selection_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 84, 4)); |
|
1245 $atom_structure['selection_duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 88, 4)); |
|
1246 $atom_structure['current_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 92, 4)); |
|
1247 $atom_structure['next_track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, 96, 4)); |
|
1248 |
|
1249 if ($atom_structure['time_scale'] == 0) { |
|
1250 $this->error('Corrupt Quicktime file: mvhd.time_scale == zero'); |
|
1251 return false; |
|
1252 } |
|
1253 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); |
|
1254 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); |
|
1255 $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']); |
|
1256 $info['quicktime']['display_scale'] = $atom_structure['matrix_a']; |
|
1257 $info['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale']; |
|
1258 break; |
|
1259 |
|
1260 |
|
1261 case 'tkhd': // TracK HeaDer atom |
|
1262 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1263 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
|
1264 $atom_structure['creation_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1265 $atom_structure['modify_time'] = getid3_lib::BigEndian2Int(substr($atom_data, 8, 4)); |
|
1266 $atom_structure['trackid'] = getid3_lib::BigEndian2Int(substr($atom_data, 12, 4)); |
|
1267 $atom_structure['reserved1'] = getid3_lib::BigEndian2Int(substr($atom_data, 16, 4)); |
|
1268 $atom_structure['duration'] = getid3_lib::BigEndian2Int(substr($atom_data, 20, 4)); |
|
1269 $atom_structure['reserved2'] = getid3_lib::BigEndian2Int(substr($atom_data, 24, 8)); |
|
1270 $atom_structure['layer'] = getid3_lib::BigEndian2Int(substr($atom_data, 32, 2)); |
|
1271 $atom_structure['alternate_group'] = getid3_lib::BigEndian2Int(substr($atom_data, 34, 2)); |
|
1272 $atom_structure['volume'] = getid3_lib::FixedPoint8_8(substr($atom_data, 36, 2)); |
|
1273 $atom_structure['reserved3'] = getid3_lib::BigEndian2Int(substr($atom_data, 38, 2)); |
|
1274 // http://developer.apple.com/library/mac/#documentation/QuickTime/RM/MovieBasics/MTEditing/K-Chapter/11MatrixFunctions.html |
|
1275 // http://developer.apple.com/library/mac/#documentation/QuickTime/qtff/QTFFChap4/qtff4.html#//apple_ref/doc/uid/TP40000939-CH206-18737 |
|
1276 $atom_structure['matrix_a'] = getid3_lib::FixedPoint16_16(substr($atom_data, 40, 4)); |
|
1277 $atom_structure['matrix_b'] = getid3_lib::FixedPoint16_16(substr($atom_data, 44, 4)); |
|
1278 $atom_structure['matrix_u'] = getid3_lib::FixedPoint2_30(substr($atom_data, 48, 4)); |
|
1279 $atom_structure['matrix_c'] = getid3_lib::FixedPoint16_16(substr($atom_data, 52, 4)); |
|
1280 $atom_structure['matrix_d'] = getid3_lib::FixedPoint16_16(substr($atom_data, 56, 4)); |
|
1281 $atom_structure['matrix_v'] = getid3_lib::FixedPoint2_30(substr($atom_data, 60, 4)); |
|
1282 $atom_structure['matrix_x'] = getid3_lib::FixedPoint16_16(substr($atom_data, 64, 4)); |
|
1283 $atom_structure['matrix_y'] = getid3_lib::FixedPoint16_16(substr($atom_data, 68, 4)); |
|
1284 $atom_structure['matrix_w'] = getid3_lib::FixedPoint2_30(substr($atom_data, 72, 4)); |
|
1285 $atom_structure['width'] = getid3_lib::FixedPoint16_16(substr($atom_data, 76, 4)); |
|
1286 $atom_structure['height'] = getid3_lib::FixedPoint16_16(substr($atom_data, 80, 4)); |
|
1287 $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001); |
|
1288 $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002); |
|
1289 $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004); |
|
1290 $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008); |
|
1291 $atom_structure['creation_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['creation_time']); |
|
1292 $atom_structure['modify_time_unix'] = getid3_lib::DateMac2Unix($atom_structure['modify_time']); |
|
1293 |
|
1294 if ($atom_structure['flags']['enabled'] == 1) { |
|
1295 if (!isset($info['video']['resolution_x']) || !isset($info['video']['resolution_y'])) { |
|
1296 $info['video']['resolution_x'] = $atom_structure['width']; |
|
1297 $info['video']['resolution_y'] = $atom_structure['height']; |
|
1298 } |
|
1299 $info['video']['resolution_x'] = max($info['video']['resolution_x'], $atom_structure['width']); |
|
1300 $info['video']['resolution_y'] = max($info['video']['resolution_y'], $atom_structure['height']); |
|
1301 $info['quicktime']['video']['resolution_x'] = $info['video']['resolution_x']; |
|
1302 $info['quicktime']['video']['resolution_y'] = $info['video']['resolution_y']; |
|
1303 } else { |
|
1304 // see: http://www.getid3.org/phpBB3/viewtopic.php?t=1295 |
|
1305 //if (isset($info['video']['resolution_x'])) { unset($info['video']['resolution_x']); } |
|
1306 //if (isset($info['video']['resolution_y'])) { unset($info['video']['resolution_y']); } |
|
1307 //if (isset($info['quicktime']['video'])) { unset($info['quicktime']['video']); } |
|
1308 } |
|
1309 break; |
|
1310 |
|
1311 |
|
1312 case 'iods': // Initial Object DeScriptor atom |
|
1313 // http://www.koders.com/c/fid1FAB3E762903DC482D8A246D4A4BF9F28E049594.aspx?s=windows.h |
|
1314 // http://libquicktime.sourcearchive.com/documentation/1.0.2plus-pdebian/iods_8c-source.html |
|
1315 $offset = 0; |
|
1316 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1317 $offset += 1; |
|
1318 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3)); |
|
1319 $offset += 3; |
|
1320 $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1321 $offset += 1; |
|
1322 $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); |
|
1323 //$offset already adjusted by quicktime_read_mp4_descr_length() |
|
1324 $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); |
|
1325 $offset += 2; |
|
1326 $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1327 $offset += 1; |
|
1328 $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1329 $offset += 1; |
|
1330 $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1331 $offset += 1; |
|
1332 $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1333 $offset += 1; |
|
1334 $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1335 $offset += 1; |
|
1336 |
|
1337 $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields |
|
1338 for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) { |
|
1339 $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1340 $offset += 1; |
1394 $offset += 1; |
1341 $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); |
1395 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 3)); |
|
1396 $offset += 3; |
|
1397 $atom_structure['mp4_iod_tag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
|
1398 $offset += 1; |
|
1399 $atom_structure['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); |
1342 //$offset already adjusted by quicktime_read_mp4_descr_length() |
1400 //$offset already adjusted by quicktime_read_mp4_descr_length() |
1343 $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); |
1401 $atom_structure['object_descriptor_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 2)); |
1344 $offset += 4; |
1402 $offset += 2; |
1345 } |
1403 $atom_structure['od_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
1346 |
1404 $offset += 1; |
1347 $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']); |
1405 $atom_structure['scene_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
1348 $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']); |
1406 $offset += 1; |
1349 break; |
1407 $atom_structure['audio_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
1350 |
1408 $offset += 1; |
1351 case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) |
1409 $atom_structure['video_profile_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
1352 $atom_structure['signature'] = substr($atom_data, 0, 4); |
1410 $offset += 1; |
1353 $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1411 $atom_structure['graphics_profile_level'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
1354 $atom_structure['fourcc'] = substr($atom_data, 8, 4); |
1412 $offset += 1; |
1355 break; |
1413 |
1356 |
1414 $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6; // 6 bytes would only be right if all tracks use 1-byte length fields |
1357 case 'mdat': // Media DATa atom |
1415 for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) { |
1358 // 'mdat' contains the actual data for the audio/video, possibly also subtitles |
1416 $atom_structure['track'][$i]['ES_ID_IncTag'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 1)); |
1359 |
1417 $offset += 1; |
1360 /* 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 */ |
1418 $atom_structure['track'][$i]['length'] = $this->quicktime_read_mp4_descr_length($atom_data, $offset); |
1361 |
1419 //$offset already adjusted by quicktime_read_mp4_descr_length() |
1362 // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?) |
1420 $atom_structure['track'][$i]['track_id'] = getid3_lib::BigEndian2Int(substr($atom_data, $offset, 4)); |
1363 $mdat_offset = 0; |
1421 $offset += 4; |
1364 while (true) { |
1422 } |
1365 if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') { |
1423 |
1366 $mdat_offset += 8; |
1424 $atom_structure['audio_profile_name'] = $this->QuicktimeIODSaudioProfileName($atom_structure['audio_profile_id']); |
1367 } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') { |
1425 $atom_structure['video_profile_name'] = $this->QuicktimeIODSvideoProfileName($atom_structure['video_profile_id']); |
1368 $mdat_offset += 8; |
1426 break; |
1369 } else { |
1427 |
1370 break; |
1428 case 'ftyp': // FileTYPe (?) atom (for MP4 it seems) |
1371 } |
1429 $atom_structure['signature'] = substr($atom_data, 0, 4); |
1372 } |
1430 $atom_structure['unknown_1'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
1373 |
1431 $atom_structure['fourcc'] = substr($atom_data, 8, 4); |
1374 // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field |
1432 break; |
1375 while (($mdat_offset < (strlen($atom_data) - 8)) |
1433 |
1376 && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) |
1434 case 'mdat': // Media DATa atom |
1377 && ($chapter_string_length < 1000) |
1435 // 'mdat' contains the actual data for the audio/video, possibly also subtitles |
1378 && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2)) |
1436 |
1379 && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) { |
1437 /* 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 */ |
1380 list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches; |
1438 |
1381 $mdat_offset += (2 + $chapter_string_length); |
1439 // first, skip any 'wide' padding, and second 'mdat' header (with specified size of zero?) |
1382 @$info['quicktime']['comments']['chapters'][] = $chapter_string; |
1440 $mdat_offset = 0; |
1383 |
1441 while (true) { |
1384 // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled) |
1442 if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') { |
1385 if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8 |
1443 $mdat_offset += 8; |
1386 $mdat_offset += 12; |
1444 } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') { |
|
1445 $mdat_offset += 8; |
|
1446 } else { |
|
1447 break; |
1387 } |
1448 } |
1388 } |
1449 } |
1389 |
1450 if (substr($atom_data, $mdat_offset, 4) == 'GPRO') { |
1390 |
1451 $GOPRO_chunk_length = getid3_lib::LittleEndian2Int(substr($atom_data, $mdat_offset + 4, 4)); |
1391 if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { |
1452 $GOPRO_offset = 8; |
1392 |
1453 $atom_structure['GPRO']['raw'] = substr($atom_data, $mdat_offset + 8, $GOPRO_chunk_length - 8); |
1393 $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8; |
1454 $atom_structure['GPRO']['firmware'] = substr($atom_structure['GPRO']['raw'], 0, 15); |
1394 $OldAVDataEnd = $info['avdataend']; |
1455 $atom_structure['GPRO']['unknown1'] = substr($atom_structure['GPRO']['raw'], 15, 16); |
1395 $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size']; |
1456 $atom_structure['GPRO']['unknown2'] = substr($atom_structure['GPRO']['raw'], 31, 32); |
1396 |
1457 $atom_structure['GPRO']['unknown3'] = substr($atom_structure['GPRO']['raw'], 63, 16); |
1397 $getid3_temp = new getID3(); |
1458 $atom_structure['GPRO']['camera'] = substr($atom_structure['GPRO']['raw'], 79, 32); |
1398 $getid3_temp->openfile($this->getid3->filename); |
1459 $info['quicktime']['camera']['model'] = rtrim($atom_structure['GPRO']['camera'], "\x00"); |
1399 $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; |
1460 } |
1400 $getid3_temp->info['avdataend'] = $info['avdataend']; |
1461 |
1401 $getid3_mp3 = new getid3_mp3($getid3_temp); |
1462 // check to see if it looks like chapter titles, in the form of unterminated strings with a leading 16-bit size field |
1402 if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) { |
1463 while (($mdat_offset < (strlen($atom_data) - 8)) |
1403 $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); |
1464 && ($chapter_string_length = getid3_lib::BigEndian2Int(substr($atom_data, $mdat_offset, 2))) |
1404 if (!empty($getid3_temp->info['warning'])) { |
1465 && ($chapter_string_length < 1000) |
1405 foreach ($getid3_temp->info['warning'] as $value) { |
1466 && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2)) |
1406 $this->warning($value); |
1467 && preg_match('#^([\x00-\xFF]{2})([\x20-\xFF]+)$#', substr($atom_data, $mdat_offset, $chapter_string_length + 2), $chapter_matches)) { |
|
1468 list($dummy, $chapter_string_length_hex, $chapter_string) = $chapter_matches; |
|
1469 $mdat_offset += (2 + $chapter_string_length); |
|
1470 @$info['quicktime']['comments']['chapters'][] = $chapter_string; |
|
1471 |
|
1472 // "encd" atom specifies encoding. In theory could be anything, almost always UTF-8, but may be UTF-16 with BOM (not currently handled) |
|
1473 if (substr($atom_data, $mdat_offset, 12) == "\x00\x00\x00\x0C\x65\x6E\x63\x64\x00\x00\x01\x00") { // UTF-8 |
|
1474 $mdat_offset += 12; |
|
1475 } |
|
1476 } |
|
1477 |
|
1478 if (($atomsize > 8) && (!isset($info['avdataend_tmp']) || ($info['quicktime'][$atomname]['size'] > ($info['avdataend_tmp'] - $info['avdataoffset'])))) { |
|
1479 |
|
1480 $info['avdataoffset'] = $atom_structure['offset'] + 8; // $info['quicktime'][$atomname]['offset'] + 8; |
|
1481 $OldAVDataEnd = $info['avdataend']; |
|
1482 $info['avdataend'] = $atom_structure['offset'] + $atom_structure['size']; // $info['quicktime'][$atomname]['offset'] + $info['quicktime'][$atomname]['size']; |
|
1483 |
|
1484 $getid3_temp = new getID3(); |
|
1485 $getid3_temp->openfile($this->getid3->filename, $this->getid3->info['filesize'], $this->getid3->fp); |
|
1486 $getid3_temp->info['avdataoffset'] = $info['avdataoffset']; |
|
1487 $getid3_temp->info['avdataend'] = $info['avdataend']; |
|
1488 $getid3_mp3 = new getid3_mp3($getid3_temp); |
|
1489 if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) { |
|
1490 $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false); |
|
1491 if (!empty($getid3_temp->info['warning'])) { |
|
1492 foreach ($getid3_temp->info['warning'] as $value) { |
|
1493 $this->warning($value); |
|
1494 } |
|
1495 } |
|
1496 if (!empty($getid3_temp->info['mpeg'])) { |
|
1497 $info['mpeg'] = $getid3_temp->info['mpeg']; |
|
1498 if (isset($info['mpeg']['audio'])) { |
|
1499 $info['audio']['dataformat'] = 'mp3'; |
|
1500 $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'))); |
|
1501 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; |
|
1502 $info['audio']['channels'] = $info['mpeg']['audio']['channels']; |
|
1503 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; |
|
1504 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); |
|
1505 $info['bitrate'] = $info['audio']['bitrate']; |
|
1506 } |
1407 } |
1507 } |
1408 } |
1508 } |
1409 if (!empty($getid3_temp->info['mpeg'])) { |
1509 unset($getid3_mp3, $getid3_temp); |
1410 $info['mpeg'] = $getid3_temp->info['mpeg']; |
1510 $info['avdataend'] = $OldAVDataEnd; |
1411 if (isset($info['mpeg']['audio'])) { |
1511 unset($OldAVDataEnd); |
1412 $info['audio']['dataformat'] = 'mp3'; |
1512 |
1413 $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'))); |
1513 } |
1414 $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate']; |
1514 |
1415 $info['audio']['channels'] = $info['mpeg']['audio']['channels']; |
1515 unset($mdat_offset, $chapter_string_length, $chapter_matches); |
1416 $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate']; |
1516 break; |
1417 $info['audio']['bitrate_mode'] = strtolower($info['mpeg']['audio']['bitrate_mode']); |
1517 |
1418 $info['bitrate'] = $info['audio']['bitrate']; |
1518 case 'free': // FREE space atom |
|
1519 case 'skip': // SKIP atom |
|
1520 case 'wide': // 64-bit expansion placeholder atom |
|
1521 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all |
|
1522 |
|
1523 // When writing QuickTime files, it is sometimes necessary to update an atom's size. |
|
1524 // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom |
|
1525 // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime |
|
1526 // puts an 8-byte placeholder atom before any atoms it may have to update the size of. |
|
1527 // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the |
|
1528 // placeholder atom can be overwritten to obtain the necessary 8 extra bytes. |
|
1529 // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). |
|
1530 break; |
|
1531 |
|
1532 |
|
1533 case 'nsav': // NoSAVe atom |
|
1534 // http://developer.apple.com/technotes/tn/tn2038.html |
|
1535 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
|
1536 break; |
|
1537 |
|
1538 case 'ctyp': // Controller TYPe atom (seen on QTVR) |
|
1539 // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt |
|
1540 // some controller names are: |
|
1541 // 0x00 + 'std' for linear movie |
|
1542 // 'none' for no controls |
|
1543 $atom_structure['ctyp'] = substr($atom_data, 0, 4); |
|
1544 $info['quicktime']['controller'] = $atom_structure['ctyp']; |
|
1545 switch ($atom_structure['ctyp']) { |
|
1546 case 'qtvr': |
|
1547 $info['video']['dataformat'] = 'quicktimevr'; |
|
1548 break; |
|
1549 } |
|
1550 break; |
|
1551 |
|
1552 case 'pano': // PANOrama track (seen on QTVR) |
|
1553 $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
|
1554 break; |
|
1555 |
|
1556 case 'hint': // HINT track |
|
1557 case 'hinf': // |
|
1558 case 'hinv': // |
|
1559 case 'hnti': // |
|
1560 $info['quicktime']['hinting'] = true; |
|
1561 break; |
|
1562 |
|
1563 case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) |
|
1564 for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) { |
|
1565 $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); |
|
1566 } |
|
1567 break; |
|
1568 |
|
1569 |
|
1570 // Observed-but-not-handled atom types are just listed here to prevent warnings being generated |
|
1571 case 'FXTC': // Something to do with Adobe After Effects (?) |
|
1572 case 'PrmA': |
|
1573 case 'code': |
|
1574 case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html |
|
1575 case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html |
|
1576 // tapt seems to be used to compute the video size [https://www.getid3.org/phpBB3/viewtopic.php?t=838] |
|
1577 // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html |
|
1578 // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html |
|
1579 case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
|
1580 case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
|
1581 case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
|
1582 case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
|
1583 //$atom_structure['data'] = $atom_data; |
|
1584 break; |
|
1585 |
|
1586 case "\xA9".'xyz': // GPS latitude+longitude+altitude |
|
1587 $atom_structure['data'] = $atom_data; |
|
1588 if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) { |
|
1589 @list($all, $latitude, $longitude, $altitude) = $matches; |
|
1590 $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude); |
|
1591 $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude); |
|
1592 if (!empty($altitude)) { |
|
1593 $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude); |
|
1594 } |
|
1595 } else { |
|
1596 $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.'); |
|
1597 } |
|
1598 break; |
|
1599 |
|
1600 case 'NCDT': |
|
1601 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
|
1602 // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 |
|
1603 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); |
|
1604 break; |
|
1605 case 'NCTH': // Nikon Camera THumbnail image |
|
1606 case 'NCVW': // Nikon Camera preVieW image |
|
1607 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
|
1608 if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) { |
|
1609 $atom_structure['data'] = $atom_data; |
|
1610 $atom_structure['image_mime'] = 'image/jpeg'; |
|
1611 $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image')); |
|
1612 $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']); |
|
1613 } |
|
1614 break; |
|
1615 case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG |
|
1616 $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); |
|
1617 break; |
|
1618 case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
|
1619 case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
|
1620 case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html |
|
1621 $atom_structure['data'] = $atom_data; |
|
1622 break; |
|
1623 |
|
1624 case "\x00\x00\x00\x00": |
|
1625 // some kind of metacontainer, may contain a big data dump such as: |
|
1626 // 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 |
|
1627 // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt |
|
1628 |
|
1629 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1630 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
|
1631 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
|
1632 //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
|
1633 break; |
|
1634 |
|
1635 case 'meta': // METAdata atom |
|
1636 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html |
|
1637 |
|
1638 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1639 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
|
1640 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
|
1641 break; |
|
1642 |
|
1643 case 'data': // metaDATA atom |
|
1644 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 |
|
1645 // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data |
|
1646 $atom_structure['language'] = substr($atom_data, 4 + 0, 2); |
|
1647 $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); |
|
1648 $atom_structure['data'] = substr($atom_data, 4 + 4); |
|
1649 $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++]; |
|
1650 |
|
1651 if ($atom_structure['key_name'] && $atom_structure['data']) { |
|
1652 @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data']; |
|
1653 } |
|
1654 break; |
|
1655 |
|
1656 case 'keys': // KEYS that may be present in the metadata atom. |
|
1657 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21 |
|
1658 // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom. |
|
1659 // 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". |
|
1660 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1661 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
|
1662 $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1663 $keys_atom_offset = 8; |
|
1664 for ($i = 1; $i <= $atom_structure['entry_count']; $i++) { |
|
1665 $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4)); |
|
1666 $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4); |
|
1667 $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8); |
|
1668 $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace |
|
1669 |
|
1670 $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value']; |
|
1671 } |
|
1672 break; |
|
1673 |
|
1674 case 'uuid': // user-defined atom often seen containing XML data, also used for potentially many other purposes, only a few specifically handled by getID3 (e.g. 360fly spatial data) |
|
1675 //Get the UUID ID in first 16 bytes |
|
1676 $uuid_bytes_read = unpack('H8time_low/H4time_mid/H4time_hi/H4clock_seq_hi/H12clock_seq_low', substr($atom_data, 0, 16)); |
|
1677 $atom_structure['uuid_field_id'] = implode('-', $uuid_bytes_read); |
|
1678 |
|
1679 switch ($atom_structure['uuid_field_id']) { // http://fileformats.archiveteam.org/wiki/Boxes/atoms_format#UUID_boxes |
|
1680 |
|
1681 case '0537cdab-9d0c-4431-a72a-fa561f2a113e': // Exif - http://fileformats.archiveteam.org/wiki/Exif |
|
1682 case '2c4c0100-8504-40b9-a03e-562148d6dfeb': // Photoshop Image Resources - http://fileformats.archiveteam.org/wiki/Photoshop_Image_Resources |
|
1683 case '33c7a4d2-b81d-4723-a0ba-f1a3e097ad38': // IPTC-IIM - http://fileformats.archiveteam.org/wiki/IPTC-IIM |
|
1684 case '8974dbce-7be7-4c51-84f9-7148f9882554': // PIFF Track Encryption Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format |
|
1685 case '96a9f1f1-dc98-402d-a7ae-d68e34451809': // GeoJP2 World File Box - http://fileformats.archiveteam.org/wiki/GeoJP2 |
|
1686 case 'a2394f52-5a9b-4f14-a244-6c427c648df4': // PIFF Sample Encryption Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format |
|
1687 case 'b14bf8bd-083d-4b43-a5ae-8cd7d5a6ce03': // GeoJP2 GeoTIFF Box - http://fileformats.archiveteam.org/wiki/GeoJP2 |
|
1688 case 'd08a4f18-10f3-4a82-b6c8-32d8aba183d3': // PIFF Protection System Specific Header Box - http://fileformats.archiveteam.org/wiki/Protected_Interoperable_File_Format |
|
1689 $this->warning('Unhandled (but recognized) "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)'); |
|
1690 break; |
|
1691 |
|
1692 case 'be7acfcb-97a9-42e8-9c71-999491e3afac': // XMP data (in XML format) |
|
1693 $atom_structure['xml'] = substr($atom_data, 16, strlen($atom_data) - 16 - 8); // 16 bytes for UUID, 8 bytes header(?) |
|
1694 break; |
|
1695 |
|
1696 case 'efe1589a-bb77-49ef-8095-27759eb1dc6f': // 360fly data |
|
1697 /* 360fly code in this block by Paul Lewis 2019-Oct-31 */ |
|
1698 /* Sensor Timestamps need to be calculated using the recordings base time at ['quicktime']['moov']['subatoms'][0]['creation_time_unix']. */ |
|
1699 $atom_structure['title'] = '360Fly Sensor Data'; |
|
1700 |
|
1701 //Get the UUID HEADER data |
|
1702 $uuid_bytes_read = unpack('vheader_size/vheader_version/vtimescale/vhardware_version/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/x/', substr($atom_data, 16, 32)); |
|
1703 $atom_structure['uuid_header'] = $uuid_bytes_read; |
|
1704 |
|
1705 $start_byte = 48; |
|
1706 $atom_SENSOR_data = substr($atom_data, $start_byte); |
|
1707 $atom_structure['sensor_data']['data_type'] = array( |
|
1708 'fusion_count' => 0, // ID 250 |
|
1709 'fusion_data' => array(), |
|
1710 'accel_count' => 0, // ID 1 |
|
1711 'accel_data' => array(), |
|
1712 'gyro_count' => 0, // ID 2 |
|
1713 'gyro_data' => array(), |
|
1714 'magno_count' => 0, // ID 3 |
|
1715 'magno_data' => array(), |
|
1716 'gps_count' => 0, // ID 5 |
|
1717 'gps_data' => array(), |
|
1718 'rotation_count' => 0, // ID 6 |
|
1719 'rotation_data' => array(), |
|
1720 'unknown_count' => 0, // ID ?? |
|
1721 'unknown_data' => array(), |
|
1722 'debug_list' => '', // Used to debug variables stored as comma delimited strings |
|
1723 ); |
|
1724 $debug_structure['debug_items'] = array(); |
|
1725 // Can start loop here to decode all sensor data in 32 Byte chunks: |
|
1726 foreach (str_split($atom_SENSOR_data, 32) as $sensor_key => $sensor_data) { |
|
1727 // This gets me a data_type code to work out what data is in the next 31 bytes. |
|
1728 $sensor_data_type = substr($sensor_data, 0, 1); |
|
1729 $sensor_data_content = substr($sensor_data, 1); |
|
1730 $uuid_bytes_read = unpack('C*', $sensor_data_type); |
|
1731 $sensor_data_array = array(); |
|
1732 switch ($uuid_bytes_read[1]) { |
|
1733 case 250: |
|
1734 $atom_structure['sensor_data']['data_type']['fusion_count']++; |
|
1735 $uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content); |
|
1736 $sensor_data_array['mode'] = $uuid_bytes_read['mode']; |
|
1737 $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp']; |
|
1738 $sensor_data_array['yaw'] = $uuid_bytes_read['yaw']; |
|
1739 $sensor_data_array['pitch'] = $uuid_bytes_read['pitch']; |
|
1740 $sensor_data_array['roll'] = $uuid_bytes_read['roll']; |
|
1741 array_push($atom_structure['sensor_data']['data_type']['fusion_data'], $sensor_data_array); |
|
1742 break; |
|
1743 case 1: |
|
1744 $atom_structure['sensor_data']['data_type']['accel_count']++; |
|
1745 $uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content); |
|
1746 $sensor_data_array['mode'] = $uuid_bytes_read['mode']; |
|
1747 $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp']; |
|
1748 $sensor_data_array['yaw'] = $uuid_bytes_read['yaw']; |
|
1749 $sensor_data_array['pitch'] = $uuid_bytes_read['pitch']; |
|
1750 $sensor_data_array['roll'] = $uuid_bytes_read['roll']; |
|
1751 array_push($atom_structure['sensor_data']['data_type']['accel_data'], $sensor_data_array); |
|
1752 break; |
|
1753 case 2: |
|
1754 $atom_structure['sensor_data']['data_type']['gyro_count']++; |
|
1755 $uuid_bytes_read = unpack('cmode/Jtimestamp/Gyaw/Gpitch/Groll/x*', $sensor_data_content); |
|
1756 $sensor_data_array['mode'] = $uuid_bytes_read['mode']; |
|
1757 $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp']; |
|
1758 $sensor_data_array['yaw'] = $uuid_bytes_read['yaw']; |
|
1759 $sensor_data_array['pitch'] = $uuid_bytes_read['pitch']; |
|
1760 $sensor_data_array['roll'] = $uuid_bytes_read['roll']; |
|
1761 array_push($atom_structure['sensor_data']['data_type']['gyro_data'], $sensor_data_array); |
|
1762 break; |
|
1763 case 3: |
|
1764 $atom_structure['sensor_data']['data_type']['magno_count']++; |
|
1765 $uuid_bytes_read = unpack('cmode/Jtimestamp/Gmagx/Gmagy/Gmagz/x*', $sensor_data_content); |
|
1766 $sensor_data_array['mode'] = $uuid_bytes_read['mode']; |
|
1767 $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp']; |
|
1768 $sensor_data_array['magx'] = $uuid_bytes_read['magx']; |
|
1769 $sensor_data_array['magy'] = $uuid_bytes_read['magy']; |
|
1770 $sensor_data_array['magz'] = $uuid_bytes_read['magz']; |
|
1771 array_push($atom_structure['sensor_data']['data_type']['magno_data'], $sensor_data_array); |
|
1772 break; |
|
1773 case 5: |
|
1774 $atom_structure['sensor_data']['data_type']['gps_count']++; |
|
1775 $uuid_bytes_read = unpack('cmode/Jtimestamp/Glat/Glon/Galt/Gspeed/nbearing/nacc/x*', $sensor_data_content); |
|
1776 $sensor_data_array['mode'] = $uuid_bytes_read['mode']; |
|
1777 $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp']; |
|
1778 $sensor_data_array['lat'] = $uuid_bytes_read['lat']; |
|
1779 $sensor_data_array['lon'] = $uuid_bytes_read['lon']; |
|
1780 $sensor_data_array['alt'] = $uuid_bytes_read['alt']; |
|
1781 $sensor_data_array['speed'] = $uuid_bytes_read['speed']; |
|
1782 $sensor_data_array['bearing'] = $uuid_bytes_read['bearing']; |
|
1783 $sensor_data_array['acc'] = $uuid_bytes_read['acc']; |
|
1784 array_push($atom_structure['sensor_data']['data_type']['gps_data'], $sensor_data_array); |
|
1785 //array_push($debug_structure['debug_items'], $uuid_bytes_read['timestamp']); |
|
1786 break; |
|
1787 case 6: |
|
1788 $atom_structure['sensor_data']['data_type']['rotation_count']++; |
|
1789 $uuid_bytes_read = unpack('cmode/Jtimestamp/Grotx/Groty/Grotz/x*', $sensor_data_content); |
|
1790 $sensor_data_array['mode'] = $uuid_bytes_read['mode']; |
|
1791 $sensor_data_array['timestamp'] = $uuid_bytes_read['timestamp']; |
|
1792 $sensor_data_array['rotx'] = $uuid_bytes_read['rotx']; |
|
1793 $sensor_data_array['roty'] = $uuid_bytes_read['roty']; |
|
1794 $sensor_data_array['rotz'] = $uuid_bytes_read['rotz']; |
|
1795 array_push($atom_structure['sensor_data']['data_type']['rotation_data'], $sensor_data_array); |
|
1796 break; |
|
1797 default: |
|
1798 $atom_structure['sensor_data']['data_type']['unknown_count']++; |
|
1799 break; |
|
1800 } |
1419 } |
1801 } |
|
1802 //if (isset($debug_structure['debug_items']) && count($debug_structure['debug_items']) > 0) { |
|
1803 // $atom_structure['sensor_data']['data_type']['debug_list'] = implode(',', $debug_structure['debug_items']); |
|
1804 //} else { |
|
1805 $atom_structure['sensor_data']['data_type']['debug_list'] = 'No debug items in list!'; |
|
1806 //} |
|
1807 break; |
|
1808 |
|
1809 default: |
|
1810 $this->warning('Unhandled "uuid" atom identified by "'.$atom_structure['uuid_field_id'].'" at offset '.$atom_structure['offset'].' ('.strlen($atom_data).' bytes)'); |
|
1811 } |
|
1812 break; |
|
1813 |
|
1814 case 'gps ': |
|
1815 // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730 |
|
1816 // The 'gps ' contains simple look up table made up of 8byte rows, that point to the 'free' atoms that contains the actual GPS data. |
|
1817 // The first row is version/metadata/notsure, I skip that. |
|
1818 // The following rows consist of 4byte address (absolute) and 4byte size (0x1000), these point to the GPS data in the file. |
|
1819 |
|
1820 $GPS_rowsize = 8; // 4 bytes for offset, 4 bytes for size |
|
1821 if (strlen($atom_data) > 0) { |
|
1822 if ((strlen($atom_data) % $GPS_rowsize) == 0) { |
|
1823 $atom_structure['gps_toc'] = array(); |
|
1824 foreach (str_split($atom_data, $GPS_rowsize) as $counter => $datapair) { |
|
1825 $atom_structure['gps_toc'][] = unpack('Noffset/Nsize', substr($atom_data, $counter * $GPS_rowsize, $GPS_rowsize)); |
|
1826 } |
|
1827 |
|
1828 $atom_structure['gps_entries'] = array(); |
|
1829 $previous_offset = $this->ftell(); |
|
1830 foreach ($atom_structure['gps_toc'] as $key => $gps_pointer) { |
|
1831 if ($key == 0) { |
|
1832 // "The first row is version/metadata/notsure, I skip that." |
|
1833 continue; |
|
1834 } |
|
1835 $this->fseek($gps_pointer['offset']); |
|
1836 $GPS_free_data = $this->fread($gps_pointer['size']); |
|
1837 |
|
1838 /* |
|
1839 // 2017-05-10: I see some of the data, notably the Hour-Minute-Second, but cannot reconcile the rest of the data. However, the NMEA "GPRMC" line is there and relatively easy to parse, so I'm using that instead |
|
1840 |
|
1841 // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730 |
|
1842 // The structure of the GPS data atom (the 'free' atoms mentioned above) is following: |
|
1843 // hour,minute,second,year,month,day,active,latitude_b,longitude_b,unknown2,latitude,longitude,speed = struct.unpack_from('<IIIIIIssssfff',data, 48) |
|
1844 // For those unfamiliar with python struct: |
|
1845 // I = int |
|
1846 // s = is string (size 1, in this case) |
|
1847 // f = float |
|
1848 |
|
1849 //$atom_structure['gps_entries'][$key] = unpack('Vhour/Vminute/Vsecond/Vyear/Vmonth/Vday/Vactive/Vlatitude_b/Vlongitude_b/Vunknown2/flatitude/flongitude/fspeed', substr($GPS_free_data, 48)); |
|
1850 */ |
|
1851 |
|
1852 // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 |
|
1853 // $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67 |
|
1854 // $GPRMC,002454,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3F |
|
1855 // $GPRMC,094347.000,A,5342.0061,N,00737.9908,W,0.01,156.75,140217,,,A*7D |
|
1856 if (preg_match('#\\$GPRMC,([0-9\\.]*),([AV]),([0-9\\.]*),([NS]),([0-9\\.]*),([EW]),([0-9\\.]*),([0-9\\.]*),([0-9]*),([0-9\\.]*),([EW]?)(,[A])?(\\*[0-9A-F]{2})#', $GPS_free_data, $matches)) { |
|
1857 $GPS_this_GPRMC = array(); |
|
1858 $GPS_this_GPRMC_raw = array(); |
|
1859 list( |
|
1860 $GPS_this_GPRMC_raw['gprmc'], |
|
1861 $GPS_this_GPRMC_raw['timestamp'], |
|
1862 $GPS_this_GPRMC_raw['status'], |
|
1863 $GPS_this_GPRMC_raw['latitude'], |
|
1864 $GPS_this_GPRMC_raw['latitude_direction'], |
|
1865 $GPS_this_GPRMC_raw['longitude'], |
|
1866 $GPS_this_GPRMC_raw['longitude_direction'], |
|
1867 $GPS_this_GPRMC_raw['knots'], |
|
1868 $GPS_this_GPRMC_raw['angle'], |
|
1869 $GPS_this_GPRMC_raw['datestamp'], |
|
1870 $GPS_this_GPRMC_raw['variation'], |
|
1871 $GPS_this_GPRMC_raw['variation_direction'], |
|
1872 $dummy, |
|
1873 $GPS_this_GPRMC_raw['checksum'], |
|
1874 ) = $matches; |
|
1875 $GPS_this_GPRMC['raw'] = $GPS_this_GPRMC_raw; |
|
1876 |
|
1877 $hour = substr($GPS_this_GPRMC['raw']['timestamp'], 0, 2); |
|
1878 $minute = substr($GPS_this_GPRMC['raw']['timestamp'], 2, 2); |
|
1879 $second = substr($GPS_this_GPRMC['raw']['timestamp'], 4, 2); |
|
1880 $ms = substr($GPS_this_GPRMC['raw']['timestamp'], 6); // may contain decimal seconds |
|
1881 $day = substr($GPS_this_GPRMC['raw']['datestamp'], 0, 2); |
|
1882 $month = substr($GPS_this_GPRMC['raw']['datestamp'], 2, 2); |
|
1883 $year = (int) substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2); |
|
1884 $year += (($year > 90) ? 1900 : 2000); // complete lack of foresight: datestamps are stored with 2-digit years, take best guess |
|
1885 $GPS_this_GPRMC['timestamp'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second.$ms; |
|
1886 |
|
1887 $GPS_this_GPRMC['active'] = ($GPS_this_GPRMC['raw']['status'] == 'A'); // A=Active,V=Void |
|
1888 |
|
1889 foreach (array('latitude','longitude') as $latlon) { |
|
1890 preg_match('#^([0-9]{1,3})([0-9]{2}\\.[0-9]+)$#', $GPS_this_GPRMC['raw'][$latlon], $matches); |
|
1891 list($dummy, $deg, $min) = $matches; |
|
1892 $GPS_this_GPRMC[$latlon] = $deg + ($min / 60); |
|
1893 } |
|
1894 $GPS_this_GPRMC['latitude'] *= (($GPS_this_GPRMC['raw']['latitude_direction'] == 'S') ? -1 : 1); |
|
1895 $GPS_this_GPRMC['longitude'] *= (($GPS_this_GPRMC['raw']['longitude_direction'] == 'W') ? -1 : 1); |
|
1896 |
|
1897 $GPS_this_GPRMC['heading'] = $GPS_this_GPRMC['raw']['angle']; |
|
1898 $GPS_this_GPRMC['speed_knot'] = $GPS_this_GPRMC['raw']['knots']; |
|
1899 $GPS_this_GPRMC['speed_kmh'] = $GPS_this_GPRMC['raw']['knots'] * 1.852; |
|
1900 if ($GPS_this_GPRMC['raw']['variation']) { |
|
1901 $GPS_this_GPRMC['variation'] = $GPS_this_GPRMC['raw']['variation']; |
|
1902 $GPS_this_GPRMC['variation'] *= (($GPS_this_GPRMC['raw']['variation_direction'] == 'W') ? -1 : 1); |
|
1903 } |
|
1904 |
|
1905 $atom_structure['gps_entries'][$key] = $GPS_this_GPRMC; |
|
1906 |
|
1907 @$info['quicktime']['gps_track'][$GPS_this_GPRMC['timestamp']] = array( |
|
1908 'latitude' => (float) $GPS_this_GPRMC['latitude'], |
|
1909 'longitude' => (float) $GPS_this_GPRMC['longitude'], |
|
1910 'speed_kmh' => (float) $GPS_this_GPRMC['speed_kmh'], |
|
1911 'heading' => (float) $GPS_this_GPRMC['heading'], |
|
1912 ); |
|
1913 |
|
1914 } else { |
|
1915 $this->warning('Unhandled GPS format in "free" atom at offset '.$gps_pointer['offset']); |
|
1916 } |
|
1917 } |
|
1918 $this->fseek($previous_offset); |
|
1919 |
|
1920 } else { |
|
1921 $this->warning('QuickTime atom "'.$atomname.'" is not mod-8 bytes long ('.$atomsize.' bytes) at offset '.$baseoffset); |
1420 } |
1922 } |
1421 } |
1923 } else { |
1422 unset($getid3_mp3, $getid3_temp); |
1924 $this->warning('QuickTime atom "'.$atomname.'" is zero bytes long at offset '.$baseoffset); |
1423 $info['avdataend'] = $OldAVDataEnd; |
1925 } |
1424 unset($OldAVDataEnd); |
1926 break; |
1425 |
1927 |
1426 } |
1928 case 'loci':// 3GP location (El Loco) |
1427 |
1929 $loffset = 0; |
1428 unset($mdat_offset, $chapter_string_length, $chapter_matches); |
1930 $info['quicktime']['comments']['gps_flags'] = array( getid3_lib::BigEndian2Int(substr($atom_data, 0, 4))); |
1429 break; |
1931 $info['quicktime']['comments']['gps_lang'] = array( getid3_lib::BigEndian2Int(substr($atom_data, 4, 2))); |
1430 |
1932 $info['quicktime']['comments']['gps_location'] = array( $this->LociString(substr($atom_data, 6), $loffset)); |
1431 case 'free': // FREE space atom |
1933 $loci_data = substr($atom_data, 6 + $loffset); |
1432 case 'skip': // SKIP atom |
1934 $info['quicktime']['comments']['gps_role'] = array( getid3_lib::BigEndian2Int(substr($loci_data, 0, 1))); |
1433 case 'wide': // 64-bit expansion placeholder atom |
1935 $info['quicktime']['comments']['gps_longitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 1, 4))); |
1434 // 'free', 'skip' and 'wide' are just padding, contains no useful data at all |
1936 $info['quicktime']['comments']['gps_latitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 5, 4))); |
1435 |
1937 $info['quicktime']['comments']['gps_altitude'] = array(getid3_lib::FixedPoint16_16(substr($loci_data, 9, 4))); |
1436 // When writing QuickTime files, it is sometimes necessary to update an atom's size. |
1938 $info['quicktime']['comments']['gps_body'] = array( $this->LociString(substr($loci_data, 13 ), $loffset)); |
1437 // It is impossible to update a 32-bit atom to a 64-bit atom since the 32-bit atom |
1939 $info['quicktime']['comments']['gps_notes'] = array( $this->LociString(substr($loci_data, 13 + $loffset), $loffset)); |
1438 // is only 8 bytes in size, and the 64-bit atom requires 16 bytes. Therefore, QuickTime |
1940 break; |
1439 // puts an 8-byte placeholder atom before any atoms it may have to update the size of. |
1941 |
1440 // In this way, if the atom needs to be converted from a 32-bit to a 64-bit atom, the |
1942 case 'chpl': // CHaPter List |
1441 // placeholder atom can be overwritten to obtain the necessary 8 extra bytes. |
1943 // https://www.adobe.com/content/dam/Adobe/en/devnet/flv/pdfs/video_file_format_spec_v10.pdf |
1442 // The placeholder atom has a type of kWideAtomPlaceholderType ( 'wide' ). |
1944 $chpl_version = getid3_lib::BigEndian2Int(substr($atom_data, 4, 1)); // Expected to be 0 |
1443 break; |
1945 $chpl_flags = getid3_lib::BigEndian2Int(substr($atom_data, 5, 3)); // Reserved, set to 0 |
1444 |
1946 $chpl_count = getid3_lib::BigEndian2Int(substr($atom_data, 8, 1)); |
1445 |
1947 $chpl_offset = 9; |
1446 case 'nsav': // NoSAVe atom |
1948 for ($i = 0; $i < $chpl_count; $i++) { |
1447 // http://developer.apple.com/technotes/tn/tn2038.html |
1949 if (($chpl_offset + 9) >= strlen($atom_data)) { |
1448 $atom_structure['data'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
1950 $this->warning('QuickTime chapter '.$i.' extends beyond end of "chpl" atom'); |
1449 break; |
1951 break; |
1450 |
1952 } |
1451 case 'ctyp': // Controller TYPe atom (seen on QTVR) |
1953 $info['quicktime']['chapters'][$i]['timestamp'] = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 8)) / 10000000; // timestamps are stored as 100-nanosecond units |
1452 // http://homepages.slingshot.co.nz/~helmboy/quicktime/formats/qtm-layout.txt |
1954 $chpl_offset += 8; |
1453 // some controller names are: |
1955 $chpl_title_size = getid3_lib::BigEndian2Int(substr($atom_data, $chpl_offset, 1)); |
1454 // 0x00 + 'std' for linear movie |
1956 $chpl_offset += 1; |
1455 // 'none' for no controls |
1957 $info['quicktime']['chapters'][$i]['title'] = substr($atom_data, $chpl_offset, $chpl_title_size); |
1456 $atom_structure['ctyp'] = substr($atom_data, 0, 4); |
1958 $chpl_offset += $chpl_title_size; |
1457 $info['quicktime']['controller'] = $atom_structure['ctyp']; |
1959 } |
1458 switch ($atom_structure['ctyp']) { |
1960 break; |
1459 case 'qtvr': |
1961 |
1460 $info['video']['dataformat'] = 'quicktimevr'; |
1962 case 'FIRM': // FIRMware version(?), seen on GoPro Hero4 |
1461 break; |
1963 $info['quicktime']['camera']['firmware'] = $atom_data; |
1462 } |
1964 break; |
1463 break; |
1965 |
1464 |
1966 case 'CAME': // FIRMware version(?), seen on GoPro Hero4 |
1465 case 'pano': // PANOrama track (seen on QTVR) |
1967 $info['quicktime']['camera']['serial_hash'] = unpack('H*', $atom_data); |
1466 $atom_structure['pano'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
1968 break; |
1467 break; |
1969 |
1468 |
1970 case 'dscp': |
1469 case 'hint': // HINT track |
1971 case 'rcif': |
1470 case 'hinf': // |
1972 // https://www.getid3.org/phpBB3/viewtopic.php?t=1908 |
1471 case 'hinv': // |
1973 if (substr($atom_data, 0, 7) == "\x00\x00\x00\x00\x55\xC4".'{') { |
1472 case 'hnti': // |
1974 if ($json_decoded = @json_decode(rtrim(substr($atom_data, 6), "\x00"), true)) { |
1473 $info['quicktime']['hinting'] = true; |
1975 $info['quicktime']['camera'][$atomname] = $json_decoded; |
1474 break; |
1976 if (($atomname == 'rcif') && isset($info['quicktime']['camera']['rcif']['wxcamera']['rotate'])) { |
1475 |
1977 $info['video']['rotate'] = $info['quicktime']['video']['rotate'] = $info['quicktime']['camera']['rcif']['wxcamera']['rotate']; |
1476 case 'imgt': // IMaGe Track reference (kQTVRImageTrackRefType) (seen on QTVR) |
1978 } |
1477 for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) { |
1979 } else { |
1478 $atom_structure['imgt'][] = getid3_lib::BigEndian2Int(substr($atom_data, $i, 4)); |
1980 $this->warning('Failed to JSON decode atom "'.$atomname.'"'); |
1479 } |
1981 $atom_structure['data'] = $atom_data; |
1480 break; |
1982 } |
1481 |
1983 unset($json_decoded); |
1482 |
1984 } else { |
1483 // Observed-but-not-handled atom types are just listed here to prevent warnings being generated |
1985 $this->warning('Expecting 55 C4 7B at start of atom "'.$atomname.'", found '.getid3_lib::PrintHexBytes(substr($atom_data, 4, 3)).' instead'); |
1484 case 'FXTC': // Something to do with Adobe After Effects (?) |
1986 $atom_structure['data'] = $atom_data; |
1485 case 'PrmA': |
1987 } |
1486 case 'code': |
1988 break; |
1487 case 'FIEL': // this is NOT "fiel" (Field Ordering) as describe here: http://developer.apple.com/documentation/QuickTime/QTFF/QTFFChap3/chapter_4_section_2.html |
1989 |
1488 case 'tapt': // TrackApertureModeDimensionsAID - http://developer.apple.com/documentation/QuickTime/Reference/QT7-1_Update_Reference/Constants/Constants.html |
1990 case 'frea': |
1489 // tapt seems to be used to compute the video size [http://www.getid3.org/phpBB3/viewtopic.php?t=838] |
1991 // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea |
1490 // * http://lists.apple.com/archives/quicktime-api/2006/Aug/msg00014.html |
1992 // may contain "scra" (PreviewImage) and/or "thma" (ThumbnailImage) |
1491 // * http://handbrake.fr/irclogs/handbrake-dev/handbrake-dev20080128_pg2.html |
1993 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); |
1492 case 'ctts':// STCompositionOffsetAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
1994 break; |
1493 case 'cslg':// STCompositionShiftLeastGreatestAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
1995 case 'tima': // subatom to "frea" |
1494 case 'sdtp':// STSampleDependencyAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
1996 // no idea what this does, the one sample file I've seen has a value of 0x00000027 |
1495 case 'stps':// STPartialSyncSampleAID - http://developer.apple.com/documentation/QuickTime/Reference/QTRef_Constants/Reference/reference.html |
|
1496 //$atom_structure['data'] = $atom_data; |
|
1497 break; |
|
1498 |
|
1499 case "\xA9".'xyz': // GPS latitude+longitude+altitude |
|
1500 $atom_structure['data'] = $atom_data; |
|
1501 if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) { |
|
1502 @list($all, $latitude, $longitude, $altitude) = $matches; |
|
1503 $info['quicktime']['comments']['gps_latitude'][] = floatval($latitude); |
|
1504 $info['quicktime']['comments']['gps_longitude'][] = floatval($longitude); |
|
1505 if (!empty($altitude)) { |
|
1506 $info['quicktime']['comments']['gps_altitude'][] = floatval($altitude); |
|
1507 } |
|
1508 } else { |
|
1509 $this->warning('QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.'. Please report as getID3() bug.'); |
|
1510 } |
|
1511 break; |
|
1512 |
|
1513 case 'NCDT': |
|
1514 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
|
1515 // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100 |
|
1516 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 4, $atomHierarchy, $ParseAllPossibleAtoms); |
|
1517 break; |
|
1518 case 'NCTH': // Nikon Camera THumbnail image |
|
1519 case 'NCVW': // Nikon Camera preVieW image |
|
1520 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
|
1521 if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) { |
|
1522 $atom_structure['data'] = $atom_data; |
1997 $atom_structure['data'] = $atom_data; |
1523 $atom_structure['image_mime'] = 'image/jpeg'; |
1998 break; |
1524 $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image')); |
1999 case 'ver ': // subatom to "frea" |
1525 $info['quicktime']['comments']['picture'][] = array('image_mime'=>$atom_structure['image_mime'], 'data'=>$atom_data, 'description'=>$atom_structure['description']); |
2000 // some kind of version number, the one sample file I've seen has a value of "3.00.073" |
1526 } |
2001 $atom_structure['data'] = $atom_data; |
1527 break; |
2002 break; |
1528 case 'NCTG': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG |
2003 case 'thma': // subatom to "frea" -- "ThumbnailImage" |
1529 $atom_structure['data'] = $this->QuicktimeParseNikonNCTG($atom_data); |
2004 // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea |
1530 break; |
2005 if (strlen($atom_data) > 0) { |
1531 case 'NCHD': // Nikon:MakerNoteVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
2006 $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'ThumbnailImage'); |
1532 case 'NCDB': // Nikon - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html |
2007 } |
1533 case 'CNCV': // Canon:CompressorVersion - http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Canon.html |
2008 break; |
1534 $atom_structure['data'] = $atom_data; |
2009 case 'scra': // subatom to "frea" -- "PreviewImage" |
1535 break; |
2010 // https://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Kodak.html#frea |
1536 |
2011 // but the only sample file I've seen has no useful data here |
1537 case "\x00\x00\x00\x00": |
2012 if (strlen($atom_data) > 0) { |
1538 // some kind of metacontainer, may contain a big data dump such as: |
2013 $info['quicktime']['comments']['picture'][] = array('data'=>$atom_data, 'image_mime'=>'image/jpeg', 'description'=>'PreviewImage'); |
1539 // 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 |
2014 } |
1540 // http://www.geocities.com/xhelmboyx/quicktime/formats/qti-layout.txt |
2015 break; |
1541 |
2016 |
1542 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
2017 case 'cdsc': // timed metadata reference |
1543 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
2018 // A QuickTime movie can contain none, one, or several timed metadata tracks. Timed metadata tracks can refer to multiple tracks. |
1544 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom(substr($atom_data, 4), $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
2019 // Metadata tracks are linked to the tracks they describe using a track-reference of type 'cdsc'. The metadata track holds the 'cdsc' track reference. |
1545 //$atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
2020 $atom_structure['track_number'] = getid3_lib::BigEndian2Int($atom_data); |
1546 break; |
2021 break; |
1547 |
2022 |
1548 case 'meta': // METAdata atom |
2023 default: |
1549 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html |
2024 $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).'), '.$atomsize.' bytes at offset '.$baseoffset); |
1550 |
2025 $atom_structure['data'] = $atom_data; |
1551 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
2026 break; |
1552 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
2027 } |
1553 $atom_structure['subatoms'] = $this->QuicktimeParseContainerAtom($atom_data, $baseoffset + 8, $atomHierarchy, $ParseAllPossibleAtoms); |
|
1554 break; |
|
1555 |
|
1556 case 'data': // metaDATA atom |
|
1557 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 |
|
1558 // seems to be 2 bytes language code (ASCII), 2 bytes unknown (set to 0x10B5 in sample I have), remainder is useful data |
|
1559 $atom_structure['language'] = substr($atom_data, 4 + 0, 2); |
|
1560 $atom_structure['unknown'] = getid3_lib::BigEndian2Int(substr($atom_data, 4 + 2, 2)); |
|
1561 $atom_structure['data'] = substr($atom_data, 4 + 4); |
|
1562 $atom_structure['key_name'] = @$info['quicktime']['temp_meta_key_names'][$metaDATAkey++]; |
|
1563 |
|
1564 if ($atom_structure['key_name'] && $atom_structure['data']) { |
|
1565 @$info['quicktime']['comments'][str_replace('com.apple.quicktime.', '', $atom_structure['key_name'])][] = $atom_structure['data']; |
|
1566 } |
|
1567 break; |
|
1568 |
|
1569 case 'keys': // KEYS that may be present in the metadata atom. |
|
1570 // https://developer.apple.com/library/mac/documentation/QuickTime/QTFF/Metadata/Metadata.html#//apple_ref/doc/uid/TP40000939-CH1-SW21 |
|
1571 // The metadata item keys atom holds a list of the metadata keys that may be present in the metadata atom. |
|
1572 // 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". |
|
1573 $atom_structure['version'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 1)); |
|
1574 $atom_structure['flags_raw'] = getid3_lib::BigEndian2Int(substr($atom_data, 1, 3)); |
|
1575 $atom_structure['entry_count'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 4)); |
|
1576 $keys_atom_offset = 8; |
|
1577 for ($i = 1; $i <= $atom_structure['entry_count']; $i++) { |
|
1578 $atom_structure['keys'][$i]['key_size'] = getid3_lib::BigEndian2Int(substr($atom_data, $keys_atom_offset + 0, 4)); |
|
1579 $atom_structure['keys'][$i]['key_namespace'] = substr($atom_data, $keys_atom_offset + 4, 4); |
|
1580 $atom_structure['keys'][$i]['key_value'] = substr($atom_data, $keys_atom_offset + 8, $atom_structure['keys'][$i]['key_size'] - 8); |
|
1581 $keys_atom_offset += $atom_structure['keys'][$i]['key_size']; // key_size includes the 4+4 bytes for key_size and key_namespace |
|
1582 |
|
1583 $info['quicktime']['temp_meta_key_names'][$i] = $atom_structure['keys'][$i]['key_value']; |
|
1584 } |
|
1585 break; |
|
1586 |
|
1587 case 'gps ': |
|
1588 // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730 |
|
1589 // The 'gps ' contains simple look up table made up of 8byte rows, that point to the 'free' atoms that contains the actual GPS data. |
|
1590 // The first row is version/metadata/notsure, I skip that. |
|
1591 // The following rows consist of 4byte address (absolute) and 4byte size (0x1000), these point to the GPS data in the file. |
|
1592 |
|
1593 $GPS_rowsize = 8; // 4 bytes for offset, 4 bytes for size |
|
1594 if (strlen($atom_data) > 0) { |
|
1595 if ((strlen($atom_data) % $GPS_rowsize) == 0) { |
|
1596 $atom_structure['gps_toc'] = array(); |
|
1597 foreach (str_split($atom_data, $GPS_rowsize) as $counter => $datapair) { |
|
1598 $atom_structure['gps_toc'][] = unpack('Noffset/Nsize', substr($atom_data, $counter * $GPS_rowsize, $GPS_rowsize)); |
|
1599 } |
|
1600 |
|
1601 $atom_structure['gps_entries'] = array(); |
|
1602 $previous_offset = $this->ftell(); |
|
1603 foreach ($atom_structure['gps_toc'] as $key => $gps_pointer) { |
|
1604 if ($key == 0) { |
|
1605 // "The first row is version/metadata/notsure, I skip that." |
|
1606 continue; |
|
1607 } |
|
1608 $this->fseek($gps_pointer['offset']); |
|
1609 $GPS_free_data = $this->fread($gps_pointer['size']); |
|
1610 |
|
1611 /* |
|
1612 // 2017-05-10: I see some of the data, notably the Hour-Minute-Second, but cannot reconcile the rest of the data. However, the NMEA "GPRMC" line is there and relatively easy to parse, so I'm using that instead |
|
1613 |
|
1614 // https://dashcamtalk.com/forum/threads/script-to-extract-gps-data-from-novatek-mp4.20808/page-2#post-291730 |
|
1615 // The structure of the GPS data atom (the 'free' atoms mentioned above) is following: |
|
1616 // hour,minute,second,year,month,day,active,latitude_b,longitude_b,unknown2,latitude,longitude,speed = struct.unpack_from('<IIIIIIssssfff',data, 48) |
|
1617 // For those unfamiliar with python struct: |
|
1618 // I = int |
|
1619 // s = is string (size 1, in this case) |
|
1620 // f = float |
|
1621 |
|
1622 //$atom_structure['gps_entries'][$key] = unpack('Vhour/Vminute/Vsecond/Vyear/Vmonth/Vday/Vactive/Vlatitude_b/Vlongitude_b/Vunknown2/flatitude/flongitude/fspeed', substr($GPS_free_data, 48)); |
|
1623 */ |
|
1624 |
|
1625 // $GPRMC,081836,A,3751.65,S,14507.36,E,000.0,360.0,130998,011.3,E*62 |
|
1626 // $GPRMC,183731,A,3907.482,N,12102.436,W,000.0,360.0,080301,015.5,E*67 |
|
1627 // $GPRMC,002454,A,3553.5295,N,13938.6570,E,0.0,43.1,180700,7.1,W,A*3F |
|
1628 // $GPRMC,094347.000,A,5342.0061,N,00737.9908,W,0.01,156.75,140217,,,A*7D |
|
1629 if (preg_match('#\\$GPRMC,([0-9\\.]*),([AV]),([0-9\\.]*),([NS]),([0-9\\.]*),([EW]),([0-9\\.]*),([0-9\\.]*),([0-9]*),([0-9\\.]*),([EW]?)(,[A])?(\\*[0-9A-F]{2})#', $GPS_free_data, $matches)) { |
|
1630 $GPS_this_GPRMC = array(); |
|
1631 list( |
|
1632 $GPS_this_GPRMC['raw']['gprmc'], |
|
1633 $GPS_this_GPRMC['raw']['timestamp'], |
|
1634 $GPS_this_GPRMC['raw']['status'], |
|
1635 $GPS_this_GPRMC['raw']['latitude'], |
|
1636 $GPS_this_GPRMC['raw']['latitude_direction'], |
|
1637 $GPS_this_GPRMC['raw']['longitude'], |
|
1638 $GPS_this_GPRMC['raw']['longitude_direction'], |
|
1639 $GPS_this_GPRMC['raw']['knots'], |
|
1640 $GPS_this_GPRMC['raw']['angle'], |
|
1641 $GPS_this_GPRMC['raw']['datestamp'], |
|
1642 $GPS_this_GPRMC['raw']['variation'], |
|
1643 $GPS_this_GPRMC['raw']['variation_direction'], |
|
1644 $dummy, |
|
1645 $GPS_this_GPRMC['raw']['checksum'], |
|
1646 ) = $matches; |
|
1647 |
|
1648 $hour = substr($GPS_this_GPRMC['raw']['timestamp'], 0, 2); |
|
1649 $minute = substr($GPS_this_GPRMC['raw']['timestamp'], 2, 2); |
|
1650 $second = substr($GPS_this_GPRMC['raw']['timestamp'], 4, 2); |
|
1651 $ms = substr($GPS_this_GPRMC['raw']['timestamp'], 6); // may contain decimal seconds |
|
1652 $day = substr($GPS_this_GPRMC['raw']['datestamp'], 0, 2); |
|
1653 $month = substr($GPS_this_GPRMC['raw']['datestamp'], 2, 2); |
|
1654 $year = substr($GPS_this_GPRMC['raw']['datestamp'], 4, 2); |
|
1655 $year += (($year > 90) ? 1900 : 2000); // complete lack of foresight: datestamps are stored with 2-digit years, take best guess |
|
1656 $GPS_this_GPRMC['timestamp'] = $year.'-'.$month.'-'.$day.' '.$hour.':'.$minute.':'.$second.$ms; |
|
1657 |
|
1658 $GPS_this_GPRMC['active'] = ($GPS_this_GPRMC['raw']['status'] == 'A'); // A=Active,V=Void |
|
1659 |
|
1660 foreach (array('latitude','longitude') as $latlon) { |
|
1661 preg_match('#^([0-9]{1,3})([0-9]{2}\\.[0-9]+)$#', $GPS_this_GPRMC['raw'][$latlon], $matches); |
|
1662 list($dummy, $deg, $min) = $matches; |
|
1663 $GPS_this_GPRMC[$latlon] = $deg + ($min / 60); |
|
1664 } |
|
1665 $GPS_this_GPRMC['latitude'] *= (($GPS_this_GPRMC['raw']['latitude_direction'] == 'S') ? -1 : 1); |
|
1666 $GPS_this_GPRMC['longitude'] *= (($GPS_this_GPRMC['raw']['longitude_direction'] == 'W') ? -1 : 1); |
|
1667 |
|
1668 $GPS_this_GPRMC['heading'] = $GPS_this_GPRMC['raw']['angle']; |
|
1669 $GPS_this_GPRMC['speed_knot'] = $GPS_this_GPRMC['raw']['knots']; |
|
1670 $GPS_this_GPRMC['speed_kmh'] = $GPS_this_GPRMC['raw']['knots'] * 1.852; |
|
1671 if ($GPS_this_GPRMC['raw']['variation']) { |
|
1672 $GPS_this_GPRMC['variation'] = $GPS_this_GPRMC['raw']['variation']; |
|
1673 $GPS_this_GPRMC['variation'] *= (($GPS_this_GPRMC['raw']['variation_direction'] == 'W') ? -1 : 1); |
|
1674 } |
|
1675 |
|
1676 $atom_structure['gps_entries'][$key] = $GPS_this_GPRMC; |
|
1677 |
|
1678 @$info['quicktime']['gps_track'][$GPS_this_GPRMC['timestamp']] = array( |
|
1679 'latitude' => $GPS_this_GPRMC['latitude'], |
|
1680 'longitude' => $GPS_this_GPRMC['longitude'], |
|
1681 'speed_kmh' => $GPS_this_GPRMC['speed_kmh'], |
|
1682 'heading' => $GPS_this_GPRMC['heading'], |
|
1683 ); |
|
1684 |
|
1685 } else { |
|
1686 $this->warning('Unhandled GPS format in "free" atom at offset '.$gps_pointer['offset']); |
|
1687 } |
|
1688 } |
|
1689 $this->fseek($previous_offset); |
|
1690 |
|
1691 } else { |
|
1692 $this->warning('QuickTime atom "'.$atomname.'" is not mod-8 bytes long ('.$atomsize.' bytes) at offset '.$baseoffset); |
|
1693 } |
|
1694 } else { |
|
1695 $this->warning('QuickTime atom "'.$atomname.'" is zero bytes long at offset '.$baseoffset); |
|
1696 } |
|
1697 break; |
|
1698 |
|
1699 case 'loci':// 3GP location (El Loco) |
|
1700 $info['quicktime']['comments']['gps_flags'] = getid3_lib::BigEndian2Int(substr($atom_data, 0, 4)); |
|
1701 $info['quicktime']['comments']['gps_lang'] = getid3_lib::BigEndian2Int(substr($atom_data, 4, 2)); |
|
1702 $loffset = 0; |
|
1703 $info['quicktime']['comments']['gps_location'] = $this->LociString(substr($atom_data, 6), $loffset); |
|
1704 $loci_data=substr($atom_data, 6 + $loffset); |
|
1705 $info['quicktime']['comments']['gps_role'] = getid3_lib::BigEndian2Int(substr($loci_data, 0, 1)); |
|
1706 $info['quicktime']['comments']['gps_longitude'] = getid3_lib::FixedPoint16_16(substr($loci_data, 1, 4)); |
|
1707 $info['quicktime']['comments']['gps_latitude'] = getid3_lib::FixedPoint16_16(substr($loci_data, 5, 4)); |
|
1708 $info['quicktime']['comments']['gps_altitude'] = getid3_lib::FixedPoint16_16(substr($loci_data, 9, 4)); |
|
1709 $info['quicktime']['comments']['gps_body'] = $this->LociString(substr($loci_data, 13), $loffset); |
|
1710 $info['quicktime']['comments']['gps_notes'] = $this->LociString(substr($loci_data, 13 + $loffset), $loffset); |
|
1711 break; |
|
1712 |
|
1713 default: |
|
1714 $this->warning('Unknown QuickTime atom type: "'.preg_replace('#[^a-zA-Z0-9 _\\-]#', '?', $atomname).'" ('.trim(getid3_lib::PrintHexBytes($atomname)).') at offset '.$baseoffset); |
|
1715 $atom_structure['data'] = $atom_data; |
|
1716 break; |
|
1717 } |
2028 } |
1718 array_pop($atomHierarchy); |
2029 array_pop($atomHierarchy); |
1719 return $atom_structure; |
2030 return $atom_structure; |
1720 } |
2031 } |
1721 |
2032 |
|
2033 /** |
|
2034 * @param string $atom_data |
|
2035 * @param int $baseoffset |
|
2036 * @param array $atomHierarchy |
|
2037 * @param bool $ParseAllPossibleAtoms |
|
2038 * |
|
2039 * @return array|false |
|
2040 */ |
1722 public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { |
2041 public function QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms) { |
1723 //echo 'QuicktimeParseContainerAtom('.substr($atom_data, 4, 4).') @ '.$baseoffset.'<br><br>'; |
|
1724 $atom_structure = false; |
2042 $atom_structure = false; |
1725 $subatomoffset = 0; |
2043 $subatomoffset = 0; |
1726 $subatomcounter = 0; |
2044 $subatomcounter = 0; |
1727 if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) { |
2045 if ((strlen($atom_data) == 4) && (getid3_lib::BigEndian2Int($atom_data) == 0x00000000)) { |
1728 return false; |
2046 return false; |