208 {
209 $info = &$this->getid3->info;
210
211 $atom_parent = array_pop($atomHierarchy);
212 array_push($atomHierarchy, $atomname);
213 $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
214 $atom_structure['name'] = $atomname;
215 $atom_structure['size'] = $atomsize;
216 $atom_structure['offset'] = $baseoffset;
217
218
219 switch ($atomname) {
220 case 'moov':
221 case 'trak':
222 case 'clip':
223 case 'matt':
224 case 'edts':
225 case 'tref':
226 case 'mdia':
227 case 'minf':
228 case 'dinf':
229 case 'udta':
230 case 'cmov':
231 case 'rmra':
232 case 'rmda':
233 case 'gmhd':
235 break;
236
237 case 'ilst':
239
240
241 $allnumericnames = true;
242 foreach ($atom_structure['subatoms'] as $subatomarray) {
243 if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
244 $allnumericnames = false;
245 break;
246 }
247 }
248 if ($allnumericnames) {
249 $newData = array();
250 foreach ($atom_structure['subatoms'] as $subatomarray) {
251 foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
252 unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
253 $newData[$subatomarray['name']] = $newData_subatomarray;
254 break;
255 }
256 }
257 $atom_structure['data'] = $newData;
258 unset($atom_structure['subatoms']);
259 }
260 break;
261
262 case "\x00\x00\x00\x01":
263 case "\x00\x00\x00\x02":
264 case "\x00\x00\x00\x03":
265 case "\x00\x00\x00\x04":
266 case "\x00\x00\x00\x05":
268 $atom_structure['name'] = $atomname;
270 break;
271
272 case 'stbl':
274 $isVideo = false;
275 $framerate = 0;
276 $framecount = 0;
277 foreach ($atom_structure['subatoms'] as $key => $value_array) {
278 if (isset($value_array['sample_description_table'])) {
279 foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
280 if (isset($value_array2['data_format'])) {
281 switch ($value_array2['data_format']) {
282 case 'avc1':
283 case 'mp4v':
284
285 $isVideo = true;
286 break;
287 case 'mp4a':
288
289 break;
290 }
291 }
292 }
293 } elseif (isset($value_array['time_to_sample_table'])) {
294 foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
295 if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
296 $framerate = round(
$info[
'quicktime'][
'time_scale'] / $value_array2[
'sample_duration'], 3);
297 $framecount = $value_array2['sample_count'];
298 }
299 }
300 }
301 }
302 if ($isVideo && $framerate) {
303 $info[
'quicktime'][
'video'][
'frame_rate'] = $framerate;
304 $info[
'video'][
'frame_rate'] =
$info[
'quicktime'][
'video'][
'frame_rate'];
305 }
306 if ($isVideo && $framecount) {
307 $info[
'quicktime'][
'video'][
'frame_count'] = $framecount;
308 }
309 break;
310
311 case 'aART':
312 case 'catg':
313 case 'covr':
314 case 'cpil':
315 case 'cprt':
316 case 'desc':
317 case 'disk':
318 case 'egid':
319 case 'gnre':
320 case 'keyw':
321 case 'ldes':
322 case 'pcst':
323 case 'pgap':
324 case 'purd':
325 case 'purl':
326 case 'rati':
327 case 'rndu':
328 case 'rpdu':
329 case 'rtng':
330 case 'stik':
331 case 'tmpo':
332 case 'trkn':
333 case 'tves':
334 case 'tvnn':
335 case 'tvsh':
336 case 'tvsn':
337 case 'akID':
338 case 'apID':
339 case 'atID':
340 case 'cmID':
341 case 'cnID':
342 case 'geID':
343 case 'plID':
344 case 'sfID':
345 case '©alb':
346 case '©art':
347 case '©ART':
348 case '©aut':
349 case '©cmt':
350 case '©com':
351 case '©cpy':
352 case '©day':
353 case '©dir':
354 case '©ed1':
355 case '©ed2':
356 case '©ed3':
357 case '©ed4':
358 case '©ed5':
359 case '©ed6':
360 case '©ed7':
361 case '©ed8':
362 case '©ed9':
363 case '©enc':
364 case '©fmt':
365 case '©gen':
366 case '©grp':
367 case '©hst':
368 case '©inf':
369 case '©lyr':
370 case '©mak':
371 case '©mod':
372 case '©nam':
373 case '©ope':
374 case '©PRD':
375 case '©prd':
376 case '©prf':
377 case '©req':
378 case '©src':
379 case '©swr':
380 case '©too':
381 case '©trk':
382 case '©url':
383 case '©wrn':
384 case '©wrt':
385 case '----':
386 if ($atom_parent == 'udta') {
387
390 $atom_structure['data'] = substr($atom_data, 4);
391
393 if (empty(
$info[
'comments'][
'language']) || (!in_array($atom_structure[
'language'],
$info[
'comments'][
'language']))) {
394 $info[
'comments'][
'language'][] = $atom_structure[
'language'];
395 }
396 } else {
397
398 $atomoffset = 0;
399 if (substr($atom_data, 2, 2) == "\x10\xB5") {
400
401
402 while ($atomoffset < strlen($atom_data)) {
404 $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
405 $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
406 switch ($boxsmalltype) {
407 case "\x10\xB5":
408 $atom_structure['data'] = $boxsmalldata;
409 break;
410 default:
411 $info[
'warning'][] =
'Unknown QuickTime smallbox type: "'.Helper::PrintHexBytes($boxsmalltype).
'" at offset '.$baseoffset;
412 $atom_structure['data'] = $atom_data;
413 break;
414 }
415 $atomoffset += (4 + $boxsmallsize);
416 }
417 } else {
418 while ($atomoffset < strlen($atom_data)) {
420 $boxtype = substr($atom_data, $atomoffset + 4, 4);
421 $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
422 if ($boxsize <= 1) {
423 $info[
'warning'][] =
'Invalid QuickTime atom box size "'.$boxsize.
'" in atom "'.$atomname.
'" at offset: '.($atom_structure[
'offset'] + $atomoffset);
424 $atom_structure['data'] = null;
425 $atomoffset = strlen($atom_data);
426 break;
427 }
428 $atomoffset += $boxsize;
429
430 switch ($boxtype) {
431 case 'mean':
432 case 'name':
433 $atom_structure[$boxtype] = substr($boxdata, 4);
434 break;
435
436 case 'data':
439 switch ($atom_structure['flags_raw']) {
440 case 0:
441 case 21:
442 switch ($atomname) {
443 case 'cpil':
444 case 'pcst':
445 case 'pgap':
447 break;
448
449 case 'tmpo':
451 break;
452
453 case 'disk':
454 case 'trkn':
457 $atom_structure['data'] = empty($num) ? '' : $num;
458 $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
459 break;
460
461 case 'gnre':
464 break;
465
466 case 'rtng':
469 break;
470
471 case 'stik':
474 break;
475
476 case 'sfID':
479 break;
480
481 case 'egid':
482 case 'purl':
483 $atom_structure['data'] = substr($boxdata, 8);
484 break;
485
486 default:
488 }
489 break;
490
491 case 1:
492 case 13:
493 default:
494 $atom_structure['data'] = substr($boxdata, 8);
495 break;
496
497 }
498 break;
499
500 default:
501 $info[
'warning'][] =
'Unknown QuickTime box type: "'.Helper::PrintHexBytes($boxtype).
'" at offset '.$baseoffset;
502 $atom_structure['data'] = $atom_data;
503
504 }
505 }
506 }
507 }
509 break;
510
511 case 'play':
513
514 $info[
'quicktime'][
'autoplay'] = $atom_structure[
'autoplay'];
515 break;
516
517 case 'WLOC':
520 break;
521
522 case 'LOOP':
523 case 'SelO':
524 case 'AllF':
526 break;
527
528 case 'name':
529 case 'MCPS':
530 case '@PRM':
531 case '@PRQ':
532 $atom_structure['data'] = $atom_data;
533 break;
534
535 case 'cmvd':
536
537
539
540 $CompressedFileData = substr($atom_data, 4);
541 if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
543 } else {
544 $info[
'warning'][] =
'Error decompressing compressed MOV atom at offset '.$atom_structure[
'offset'];
545 }
546 break;
547
548 case 'dcom':
549 $atom_structure['compression_id'] = $atom_data;
551 break;
552
553 case 'rdrf':
556 $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
557
558 $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
560 switch ($atom_structure['reference_type_name']) {
561 case 'url ':
562 $atom_structure[
'url'] = $this->
NoNullString(substr($atom_data, 12));
563 break;
564
565 case 'alis':
566 $atom_structure['file_alias'] = substr($atom_data, 12);
567 break;
568
569 case 'rsrc':
570 $atom_structure['resource_alias'] = substr($atom_data, 12);
571 break;
572
573 default:
574 $atom_structure['data'] = substr($atom_data, 12);
575 break;
576 }
577 break;
578
579 case 'rmqu':
581 break;
582
583 case 'rmcs':
587 break;
588
589 case 'rmvc':
592 $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
596 break;
597
598 case 'rmcd':
601 $atom_structure['component_type'] = substr($atom_data, 4, 4);
602 $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
603 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
607 break;
608
609 case 'rmdr':
613
614 $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
615 break;
616
617 case 'rmla':
621
623 if (empty(
$info[
'comments'][
'language']) || (!in_array($atom_structure[
'language'],
$info[
'comments'][
'language']))) {
624 $info[
'comments'][
'language'][] = $atom_structure[
'language'];
625 }
626 break;
627
628 case 'rmla':
632 break;
633
634 case 'ptv ':
635
641
642 $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
643 $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
644
645 $ptv_lookup[0] = 'normal';
646 $ptv_lookup[1] = 'double';
647 $ptv_lookup[2] = 'half';
648 $ptv_lookup[3] = 'full';
649 $ptv_lookup[4] = 'current';
650 if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
651 $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
652 } else {
653 $info[
'warning'][] =
'unknown "ptv " display constant ('.$atom_structure[
'display_size_raw'].
')';
654 }
655 break;
656
657
658 case 'stsd':
662 $stsdEntriesDataOffset = 8;
663 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
664 $atom_structure[
'sample_description_table'][$i][
'size'] =
Helper::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
665 $stsdEntriesDataOffset += 4;
666 $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
667 $stsdEntriesDataOffset += 4;
668 $atom_structure[
'sample_description_table'][$i][
'reserved'] =
Helper::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
669 $stsdEntriesDataOffset += 6;
670 $atom_structure[
'sample_description_table'][$i][
'reference_index'] =
Helper::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
671 $stsdEntriesDataOffset += 2;
672 $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
673 $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
674
675 $atom_structure[
'sample_description_table'][$i][
'encoder_version'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 0, 2));
676 $atom_structure[
'sample_description_table'][$i][
'encoder_revision'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 2, 2));
677 $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
678
679 switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
680
681 case "\x00\x00\x00\x00":
682
683 $atom_structure[
'sample_description_table'][$i][
'audio_channels'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 8, 2));
684 $atom_structure[
'sample_description_table'][$i][
'audio_bit_depth'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 10, 2));
685 $atom_structure[
'sample_description_table'][$i][
'audio_compression_id'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 12, 2));
686 $atom_structure[
'sample_description_table'][$i][
'audio_packet_size'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 14, 2));
687 $atom_structure[
'sample_description_table'][$i][
'audio_sample_rate'] =
Helper::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 16, 4));
688
689 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
690 case 'avc1':
691 case 'mp4v':
692 $info[
'fileformat'] =
'mp4';
693 $info[
'video'][
'fourcc'] = $atom_structure[
'sample_description_table'][$i][
'data_format'];
694
695 break;
696
697 case 'qtvr':
698 $info[
'video'][
'dataformat'] =
'quicktimevr';
699 break;
700
701 case 'mp4a':
702 default:
704 $info[
'quicktime'][
'audio'][
'sample_rate'] = $atom_structure[
'sample_description_table'][$i][
'audio_sample_rate'];
705 $info[
'quicktime'][
'audio'][
'channels'] = $atom_structure[
'sample_description_table'][$i][
'audio_channels'];
706 $info[
'quicktime'][
'audio'][
'bit_depth'] = $atom_structure[
'sample_description_table'][$i][
'audio_bit_depth'];
707 $info[
'audio'][
'codec'] =
$info[
'quicktime'][
'audio'][
'codec'];
708 $info[
'audio'][
'sample_rate'] =
$info[
'quicktime'][
'audio'][
'sample_rate'];
709 $info[
'audio'][
'channels'] =
$info[
'quicktime'][
'audio'][
'channels'];
710 $info[
'audio'][
'bits_per_sample'] =
$info[
'quicktime'][
'audio'][
'bit_depth'];
711 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
712 case 'raw ':
713 case 'alac':
714 $info[
'audio'][
'lossless'] =
true;
715 break;
716 default:
717 $info[
'audio'][
'lossless'] =
false;
718 break;
719 }
720 break;
721 }
722 break;
723
724 default:
725 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
726 case 'mp4s':
727 $info[
'fileformat'] =
'mp4';
728 break;
729
730 default:
731
732 $atom_structure[
'sample_description_table'][$i][
'video_temporal_quality'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 8, 4));
733 $atom_structure[
'sample_description_table'][$i][
'video_spatial_quality'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 12, 4));
734 $atom_structure[
'sample_description_table'][$i][
'video_frame_width'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 16, 2));
735 $atom_structure[
'sample_description_table'][$i][
'video_frame_height'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 18, 2));
736 $atom_structure[
'sample_description_table'][$i][
'video_resolution_x'] =
Helper::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 20, 4));
737 $atom_structure[
'sample_description_table'][$i][
'video_resolution_y'] =
Helper::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 24, 4));
738 $atom_structure[
'sample_description_table'][$i][
'video_data_size'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 28, 4));
739 $atom_structure[
'sample_description_table'][$i][
'video_frame_count'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 32, 2));
740 $atom_structure[
'sample_description_table'][$i][
'video_encoder_name_len'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 34, 1));
741 $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']);
742 $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_depth'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 66, 2));
743 $atom_structure[
'sample_description_table'][$i][
'video_color_table_id'] =
Helper::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 68, 2));
744
745 $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
746 $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_name'] = $this->
QuicktimeColorNameLookup($atom_structure[
'sample_description_table'][$i][
'video_pixel_color_depth']);
747
748 if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
749 $info[
'quicktime'][
'video'][
'codec_fourcc'] = $atom_structure[
'sample_description_table'][$i][
'data_format'];
750 $info[
'quicktime'][
'video'][
'codec_fourcc_lookup'] = $this->
QuicktimeVideoCodecLookup($atom_structure[
'sample_description_table'][$i][
'data_format']);
751 $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']);
752 $info[
'quicktime'][
'video'][
'color_depth'] = $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_depth'];
753 $info[
'quicktime'][
'video'][
'color_depth_name'] = $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_name'];
754
755 $info[
'video'][
'codec'] =
$info[
'quicktime'][
'video'][
'codec'];
756 $info[
'video'][
'bits_per_sample'] =
$info[
'quicktime'][
'video'][
'color_depth'];
757 }
758 $info[
'video'][
'lossless'] =
false;
759 $info[
'video'][
'pixel_aspect_ratio'] = (float) 1;
760 break;
761 }
762 break;
763 }
764 switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
765 case 'mp4a':
766 $info[
'audio'][
'dataformat'] =
'mp4';
767 $info[
'quicktime'][
'audio'][
'codec'] =
'mp4';
768 break;
769
770 case '3ivx':
771 case '3iv1':
772 case '3iv2':
773 $info[
'video'][
'dataformat'] =
'3ivx';
774 break;
775
776 case 'xvid':
777 $info[
'video'][
'dataformat'] =
'xvid';
778 break;
779
780 case 'mp4v':
781 $info[
'video'][
'dataformat'] =
'mpeg4';
782 break;
783
784 case 'divx':
785 case 'div1':
786 case 'div2':
787 case 'div3':
788 case 'div4':
789 case 'div5':
790 case 'div6':
791 $info[
'video'][
'dataformat'] =
'divx';
792 break;
793
794 default:
795
796 break;
797 }
798 unset($atom_structure['sample_description_table'][$i]['data']);
799 }
800 break;
801
802 case 'stts':
806 $sttsEntriesDataOffset = 8;
807
808 $frames_count = 0;
809 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
810 $atom_structure[
'time_to_sample_table'][$i][
'sample_count'] =
Helper::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
811 $sttsEntriesDataOffset += 4;
812 $atom_structure[
'time_to_sample_table'][$i][
'sample_duration'] =
Helper::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
813 $sttsEntriesDataOffset += 4;
814
815 $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
816
817
818
819
820
821
822
823
824
825
826
827 }
828 $info[
'quicktime'][
'stts_framecount'][] = $frames_count;
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846 break;
847
848 case 'stss':
853 $stssEntriesDataOffset = 8;
854 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
855 $atom_structure[
'time_to_sample_table'][$i] =
Helper::BigEndian2Int(substr($atom_data, $stssEntriesDataOffset, 4));
856 $stssEntriesDataOffset += 4;
857 }
858 }
859 break;
860
861 case 'stsc':
866 $stscEntriesDataOffset = 8;
867 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
868 $atom_structure[
'sample_to_chunk_table'][$i][
'first_chunk'] =
Helper::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
869 $stscEntriesDataOffset += 4;
870 $atom_structure[
'sample_to_chunk_table'][$i][
'samples_per_chunk'] =
Helper::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
871 $stscEntriesDataOffset += 4;
872 $atom_structure[
'sample_to_chunk_table'][$i][
'sample_description'] =
Helper::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
873 $stscEntriesDataOffset += 4;
874 }
875 }
876 break;
877
878 case 'stsz':
884 $stszEntriesDataOffset = 12;
885 if ($atom_structure['sample_size'] == 0) {
886 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
887 $atom_structure[
'sample_size_table'][$i] =
Helper::BigEndian2Int(substr($atom_data, $stszEntriesDataOffset, 4));
888 $stszEntriesDataOffset += 4;
889 }
890 }
891 }
892 break;
893
894 case 'stco':
899 $stcoEntriesDataOffset = 8;
900 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
901 $atom_structure[
'chunk_offset_table'][$i] =
Helper::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 4));
902 $stcoEntriesDataOffset += 4;
903 }
904 }
905 break;
906
907 case 'co64':
912 $stcoEntriesDataOffset = 8;
913 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
914 $atom_structure[
'chunk_offset_table'][$i] =
Helper::BigEndian2Int(substr($atom_data, $stcoEntriesDataOffset, 8));
915 $stcoEntriesDataOffset += 8;
916 }
917 }
918 break;
919
920 case 'dref':
924 $drefDataOffset = 8;
925 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
926 $atom_structure[
'data_references'][$i][
'size'] =
Helper::BigEndian2Int(substr($atom_data, $drefDataOffset, 4));
927 $drefDataOffset += 4;
928 $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
929 $drefDataOffset += 4;
930 $atom_structure[
'data_references'][$i][
'version'] =
Helper::BigEndian2Int(substr($atom_data, $drefDataOffset, 1));
931 $drefDataOffset += 1;
932 $atom_structure[
'data_references'][$i][
'flags_raw'] =
Helper::BigEndian2Int(substr($atom_data, $drefDataOffset, 3));
933 $drefDataOffset += 3;
934 $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
935 $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
936
937 $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
938 }
939 break;
940
941 case 'gmin':
950 break;
951
952 case 'smhd':
957 break;
958
959 case 'vmhd':
966
967 $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
968 break;
969
970 case 'hdlr':
973 $atom_structure['component_type'] = substr($atom_data, 4, 4);
974 $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
975 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
978 $atom_structure[
'component_name'] = $this->
Pascal2String(substr($atom_data, 24));
979
980 if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
981 $info[
'video'][
'dataformat'] =
'quicktimevr';
982 }
983 break;
984
985 case 'mdhd':
994
995 if ($atom_structure['time_scale'] == 0) {
996 $info[
'error'][] =
'Corrupt Quicktime file: mdhd.time_scale == zero';
997
998 return false;
999 }
1000 $info[
'quicktime'][
'time_scale'] = (isset(
$info[
'quicktime'][
'time_scale']) ? max(
$info[
'quicktime'][
'time_scale'], $atom_structure[
'time_scale']) : $atom_structure[
'time_scale']);
1001
1002 $atom_structure[
'creation_time_unix'] =
Helper::DateMac2Unix($atom_structure[
'creation_time']);
1004 $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1006 if (empty(
$info[
'comments'][
'language']) || (!in_array($atom_structure[
'language'],
$info[
'comments'][
'language']))) {
1007 $info[
'comments'][
'language'][] = $atom_structure[
'language'];
1008 }
1009 break;
1010
1011 case 'pnot':
1014 $atom_structure['atom_type'] = substr($atom_data, 6, 4);
1016
1017 $atom_structure[
'modification_date_unix'] =
Helper::DateMac2Unix($atom_structure[
'modification_date']);
1018 break;
1019
1020 case 'crgn':
1023 $atom_structure['clipping_data'] = substr($atom_data, 10);
1024 break;
1025
1026 case 'load':
1031
1032 $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
1033 $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
1034 break;
1035
1036 case 'tmcd':
1037 case 'chap':
1038 case 'sync':
1039 case 'scpt':
1040 case 'ssrc':
1041 for ($i = 0; $i < (strlen($atom_data) % 4); $i++) {
1043 }
1044 break;
1045
1046 case 'elst':
1050 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
1051 $atom_structure[
'edit_list'][$i][
'track_duration'] =
Helper::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
1052 $atom_structure[
'edit_list'][$i][
'media_time'] =
Helper::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 4, 4));
1053 $atom_structure[
'edit_list'][$i][
'media_rate'] =
Helper::FixedPoint16_16(substr($atom_data, 8 + ($i * 12) + 8, 4));
1054 }
1055 break;
1056
1057 case 'kmat':
1060 $atom_structure['matte_data_raw'] = substr($atom_data, 4);
1061 break;
1062
1063 case 'ctab':
1067 for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
1068 $atom_structure[
'color_table'][$colortableentry][
'alpha'] =
Helper::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
1069 $atom_structure[
'color_table'][$colortableentry][
'red'] =
Helper::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
1070 $atom_structure[
'color_table'][$colortableentry][
'green'] =
Helper::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
1071 $atom_structure[
'color_table'][$colortableentry][
'blue'] =
Helper::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
1072 }
1073 break;
1074
1075 case 'mvhd':
1084 $atom_structure['reserved'] = substr($atom_data, 26, 10);
1101
1102 if ($atom_structure['time_scale'] == 0) {
1103 $info[
'error'][] =
'Corrupt Quicktime file: mvhd.time_scale == zero';
1104
1105 return false;
1106 }
1107 $atom_structure[
'creation_time_unix'] =
Helper::DateMac2Unix($atom_structure[
'creation_time']);
1109 $info[
'quicktime'][
'time_scale'] = (isset(
$info[
'quicktime'][
'time_scale']) ? max(
$info[
'quicktime'][
'time_scale'], $atom_structure[
'time_scale']) : $atom_structure[
'time_scale']);
1110 $info[
'quicktime'][
'display_scale'] = $atom_structure[
'matrix_a'];
1111 $info[
'playtime_seconds'] = $atom_structure[
'duration'] / $atom_structure[
'time_scale'];
1112 break;
1113
1114 case 'tkhd':
1138
1139 $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001);
1140 $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002);
1141 $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
1142 $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
1143 $atom_structure[
'creation_time_unix'] =
Helper::DateMac2Unix($atom_structure[
'creation_time']);
1145
1146 if ($atom_structure['flags']['enabled'] == 1) {
1147 if (!isset(
$info[
'video'][
'resolution_x']) || !isset(
$info[
'video'][
'resolution_y'])) {
1148 $info[
'video'][
'resolution_x'] = $atom_structure[
'width'];
1149 $info[
'video'][
'resolution_y'] = $atom_structure[
'height'];
1150 }
1151 $info[
'video'][
'resolution_x'] = max(
$info[
'video'][
'resolution_x'], $atom_structure[
'width']);
1152 $info[
'video'][
'resolution_y'] = max(
$info[
'video'][
'resolution_y'], $atom_structure[
'height']);
1153 $info[
'quicktime'][
'video'][
'resolution_x'] =
$info[
'video'][
'resolution_x'];
1154 $info[
'quicktime'][
'video'][
'resolution_y'] =
$info[
'video'][
'resolution_y'];
1155 } else {
1156 if (isset(
$info[
'video'][
'resolution_x'])) { unset(
$info[
'video'][
'resolution_x']); }
1157 if (isset(
$info[
'video'][
'resolution_y'])) { unset(
$info[
'video'][
'resolution_y']); }
1158 if (isset(
$info[
'quicktime'][
'video'])) { unset(
$info[
'quicktime'][
'video']); }
1159 }
1160 break;
1161
1162 case 'iods':
1163
1164
1165 $offset = 0;
1167 $offset += 1;
1169 $offset += 3;
1171 $offset += 1;
1173
1175 $offset += 2;
1177 $offset += 1;
1179 $offset += 1;
1181 $offset += 1;
1183 $offset += 1;
1185 $offset += 1;
1186
1187 $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6;
1188 for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
1189 $atom_structure[
'track'][$i][
'ES_ID_IncTag'] =
Helper::BigEndian2Int(substr($atom_data, $offset, 1));
1190 $offset += 1;
1192
1194 $offset += 4;
1195 }
1196
1199 break;
1200
1201 case 'ftyp':
1202 $atom_structure['signature'] = substr($atom_data, 0, 4);
1204 $atom_structure['fourcc'] = substr($atom_data, 8, 4);
1205 break;
1206
1207 case 'mdat':
1208 case 'free':
1209 case 'skip':
1210 case 'wide':
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221 break;
1222
1223
1224 case 'nsav':
1225
1227 break;
1228
1229 case 'ctyp':
1230
1231
1232
1233
1234 $atom_structure['ctyp'] = substr($atom_data, 0, 4);
1235 $info[
'quicktime'][
'controller'] = $atom_structure[
'ctyp'];
1236 switch ($atom_structure['ctyp']) {
1237 case 'qtvr':
1238 $info[
'video'][
'dataformat'] =
'quicktimevr';
1239 break;
1240 }
1241 break;
1242
1243 case 'pano':
1245 break;
1246
1247 case 'hint':
1248 case 'hinf':
1249 case 'hinv':
1250 case 'hnti':
1251 $info[
'quicktime'][
'hinting'] =
true;
1252 break;
1253
1254 case 'imgt':
1255 for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1257 }
1258 break;
1259
1260
1261
1262 case 'FXTC':
1263 case 'PrmA':
1264 case 'code':
1265 case 'FIEL':
1266 case 'tapt':
1267
1268
1269
1270 case 'ctts':
1271 case 'cslg':
1272 case 'sdtp':
1273 case 'stps':
1274
1275 break;
1276
1277 case '©xyz':
1278 $atom_structure['data'] = $atom_data;
1279 if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
1280 @list($all, $latitude, $longitude, $altitude) = $matches;
1281 $info[
'quicktime'][
'comments'][
'gps_latitude'][] = floatval($latitude);
1282 $info[
'quicktime'][
'comments'][
'gps_longitude'][] = floatval($longitude);
1283 if (!empty($altitude)) {
1284 $info[
'quicktime'][
'comments'][
'gps_altitude'][] = floatval($altitude);
1285 }
1286 } else {
1287 $info[
'warning'][] =
'QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.
'. Please report as GetId3Core() bug.';
1288 }
1289 break;
1290
1291 case 'NCDT':
1292
1293
1295 break;
1296 case 'NCTH':
1297 case 'NCVW':
1298
1299 if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
1300 $atom_structure['data'] = $atom_data;
1301 $atom_structure['image_mime'] = 'image/jpeg';
1302 $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
1303 $info[
'quicktime'][
'comments'][
'picture'][] = array(
'image_mime'=>$atom_structure[
'image_mime'],
'data'=>$atom_data,
'description'=>$atom_structure[
'description']);
1304 }
1305 break;
1306 case 'NCHD':
1307
1308 $atom_structure['data'] = $atom_data;
1309 break;
1310 case 'NCTG':
1311
1313 break;
1314 case 'NCDB':
1315
1316 $atom_structure['data'] = $atom_data;
1317 break;
1318
1319 case "\x00\x00\x00\x00":
1320 case 'meta':
1321
1322
1323
1324
1328
1329 break;
1330
1331 case 'data':
1332
1333 $atom_structure['language'] = substr($atom_data, 4 + 0, 2);
1335 $atom_structure['data'] = substr($atom_data, 4 + 4);
1336 break;
1337
1338 default:
1339 $info[
'warning'][] =
'Unknown QuickTime atom type: "'.$atomname.
'" ('.trim(
Helper::PrintHexBytes($atomname)).
') at offset '.$baseoffset;
1340 $atom_structure['data'] = $atom_data;
1341 break;
1342 }
1343 array_pop($atomHierarchy);
1344
1345 return $atom_structure;
1346 }
static FixedPoint16_16($rawdata)
static PrintHexBytes($string, $hex=true, $spaces=true, $htmlencoding='UTF-8')
static FixedPoint2_30($rawdata)
static FixedPoint8_8($rawdata)
static DateMac2Unix($macdate)
QuicktimeColorNameLookup($colordepthid)
@staticvar array $QuicktimeColorNameLookup
QuicktimeDCOMLookup($compressionid)
@staticvar array $QuicktimeDCOMLookup
QuicktimeSTIKLookup($stik)
@staticvar array $QuicktimeSTIKLookup
QuicktimeAudioCodecLookup($codecid)
@staticvar array $QuicktimeAudioCodecLookup
QuicktimeIODSvideoProfileName($video_profile_id)
@staticvar array $QuicktimeIODSvideoProfileNameLookup
QuicktimeIODSaudioProfileName($audio_profile_id)
@staticvar array $QuicktimeIODSaudioProfileNameLookup
QuicktimeStoreFrontCodeLookup($sfid)
@staticvar array $QuicktimeStoreFrontCodeLookup
QuicktimeLanguageLookup($languageid)
@staticvar array $QuicktimeLanguageLookup
CopyToAppropriateCommentsSection($keyname, $data, $boxname='')
@staticvar array $handyatomtranslatorarray
QuicktimeContentRatingLookup($rtng)
@staticvar array $QuicktimeContentRatingLookup
QuicktimeParseNikonNCTG($atom_data)
Pascal2String($pascalstring)
QuicktimeParseContainerAtom($atom_data, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms)
NoNullString($nullterminatedstring)
quicktime_read_mp4_descr_length($data, &$offset)
QuicktimeVideoCodecLookup($codecid)
@staticvar array $QuicktimeVideoCodecLookup
static LookupGenreName($genreid, $allowSCMPXextended=true)