121 {
122
123
124
125 $info = &$this->getid3->info;
126
127 $atom_parent = end($atomHierarchy);
128 array_push($atomHierarchy, $atomname);
129 $atom_structure['hierarchy'] = implode(' ', $atomHierarchy);
130 $atom_structure['name'] = $atomname;
131 $atom_structure['size'] = $atomsize;
132 $atom_structure['offset'] = $baseoffset;
133 switch ($atomname) {
134 case 'moov':
135 case 'trak':
136 case 'clip':
137 case 'matt':
138 case 'edts':
139 case 'tref':
140 case 'mdia':
141 case 'minf':
142 case 'dinf':
143 case 'udta':
144 case 'cmov':
145 case 'rmra':
146 case 'rmda':
147 case 'gmhd':
149 break;
150
151 case 'ilst':
153
154 $allnumericnames = true;
155 foreach ($atom_structure['subatoms'] as $subatomarray) {
156 if (!is_integer($subatomarray['name']) || (count($subatomarray['subatoms']) != 1)) {
157 $allnumericnames = false;
158 break;
159 }
160 }
161 if ($allnumericnames) {
162 $newData = array();
163 foreach ($atom_structure['subatoms'] as $subatomarray) {
164 foreach ($subatomarray['subatoms'] as $newData_subatomarray) {
165 unset($newData_subatomarray['hierarchy'], $newData_subatomarray['name']);
166 $newData[$subatomarray['name']] = $newData_subatomarray;
167 break;
168 }
169 }
170 $atom_structure['data'] = $newData;
171 unset($atom_structure['subatoms']);
172 }
173 }
174 break;
175
176 case "\x00\x00\x00\x01":
177 case "\x00\x00\x00\x02":
178 case "\x00\x00\x00\x03":
179 case "\x00\x00\x00\x04":
180 case "\x00\x00\x00\x05":
182 $atom_structure['name'] = $atomname;
184 break;
185
186 case 'stbl':
188 $isVideo = false;
189 $framerate = 0;
190 $framecount = 0;
191 foreach ($atom_structure['subatoms'] as $key => $value_array) {
192 if (isset($value_array['sample_description_table'])) {
193 foreach ($value_array['sample_description_table'] as $key2 => $value_array2) {
194 if (isset($value_array2['data_format'])) {
195 switch ($value_array2['data_format']) {
196 case 'avc1':
197 case 'mp4v':
198
199 $isVideo = true;
200 break;
201 case 'mp4a':
202
203 break;
204 }
205 }
206 }
207 } elseif (isset($value_array['time_to_sample_table'])) {
208 foreach ($value_array['time_to_sample_table'] as $key2 => $value_array2) {
209 if (isset($value_array2['sample_count']) && isset($value_array2['sample_duration']) && ($value_array2['sample_duration'] > 0)) {
210 $framerate = round(
$info[
'quicktime'][
'time_scale'] / $value_array2[
'sample_duration'], 3);
211 $framecount = $value_array2['sample_count'];
212 }
213 }
214 }
215 }
216 if ($isVideo && $framerate) {
217 $info[
'quicktime'][
'video'][
'frame_rate'] = $framerate;
218 $info[
'video'][
'frame_rate'] =
$info[
'quicktime'][
'video'][
'frame_rate'];
219 }
220 if ($isVideo && $framecount) {
221 $info[
'quicktime'][
'video'][
'frame_count'] = $framecount;
222 }
223 break;
224
225
226 case "\xA9".'alb':
227 case "\xA9".'ART':
228 case "\xA9".'art':
229 case "\xA9".'aut':
230 case "\xA9".'cmt':
231 case "\xA9".'com':
232 case "\xA9".'cpy':
233 case "\xA9".'day':
234 case "\xA9".'dir':
235 case "\xA9".'ed1':
236 case "\xA9".'ed2':
237 case "\xA9".'ed3':
238 case "\xA9".'ed4':
239 case "\xA9".'ed5':
240 case "\xA9".'ed6':
241 case "\xA9".'ed7':
242 case "\xA9".'ed8':
243 case "\xA9".'ed9':
244 case "\xA9".'enc':
245 case "\xA9".'fmt':
246 case "\xA9".'gen':
247 case "\xA9".'grp':
248 case "\xA9".'hst':
249 case "\xA9".'inf':
250 case "\xA9".'lyr':
251 case "\xA9".'mak':
252 case "\xA9".'mod':
253 case "\xA9".'nam':
254 case "\xA9".'ope':
255 case "\xA9".'PRD':
256 case "\xA9".'prf':
257 case "\xA9".'req':
258 case "\xA9".'src':
259 case "\xA9".'swr':
260 case "\xA9".'too':
261 case "\xA9".'trk':
262 case "\xA9".'url':
263 case "\xA9".'wrn':
264 case "\xA9".'wrt':
265 case '----':
266 case 'aART':
267 case 'akID':
268 case 'apID':
269 case 'atID':
270 case 'catg':
271 case 'cmID':
272 case 'cnID':
273 case 'covr':
274 case 'cpil':
275 case 'cprt':
276 case 'desc':
277 case 'disk':
278 case 'egid':
279 case 'geID':
280 case 'gnre':
281 case 'hdvd':
282 case 'keyw':
283 case 'ldes':
284 case 'pcst':
285 case 'pgap':
286 case 'plID':
287 case 'purd':
288 case 'purl':
289 case 'rati':
290 case 'rndu':
291 case 'rpdu':
292 case 'rtng':
293 case 'sfID':
294 case 'soaa':
295 case 'soal':
296 case 'soar':
297 case 'soco':
298 case 'sonm':
299 case 'sosn':
300 case 'stik':
301 case 'tmpo':
302 case 'trkn':
303 case 'tven':
304 case 'tves':
305 case 'tvnn':
306 case 'tvsh':
307 case 'tvsn':
308 if ($atom_parent == 'udta') {
309
312 $atom_structure['data'] = substr($atom_data, 4);
313
315 if (empty(
$info[
'comments'][
'language']) || (!in_array($atom_structure[
'language'],
$info[
'comments'][
'language']))) {
316 $info[
'comments'][
'language'][] = $atom_structure[
'language'];
317 }
318 } else {
319
320 $atomoffset = 0;
321 if (substr($atom_data, 2, 2) == "\x10\xB5") {
322
323
324 while ($atomoffset < strlen($atom_data)) {
326 $boxsmalltype = substr($atom_data, $atomoffset + 2, 2);
327 $boxsmalldata = substr($atom_data, $atomoffset + 4, $boxsmallsize);
328 if ($boxsmallsize <= 1) {
329 $info[
'warning'][] =
'Invalid QuickTime atom smallbox size "'.$boxsmallsize.
'" in atom "'.preg_replace(
'#[^a-zA-Z0-9 _\\-]#',
'?', $atomname).
'" at offset: '.($atom_structure[
'offset'] + $atomoffset);
330 $atom_structure['data'] = null;
331 $atomoffset = strlen($atom_data);
332 break;
333 }
334 switch ($boxsmalltype) {
335 case "\x10\xB5":
336 $atom_structure['data'] = $boxsmalldata;
337 break;
338 default:
339 $info[
'warning'][] =
'Unknown QuickTime smallbox type: "'.preg_replace(
'#[^a-zA-Z0-9 _\\-]#',
'?', $boxsmalltype).
'" ('.trim(
getid3_lib::PrintHexBytes($boxsmalltype)).
') at offset '.$baseoffset;
340 $atom_structure['data'] = $atom_data;
341 break;
342 }
343 $atomoffset += (4 + $boxsmallsize);
344 }
345 } else {
346 while ($atomoffset < strlen($atom_data)) {
348 $boxtype = substr($atom_data, $atomoffset + 4, 4);
349 $boxdata = substr($atom_data, $atomoffset + 8, $boxsize - 8);
350 if ($boxsize <= 1) {
351 $info[
'warning'][] =
'Invalid QuickTime atom box size "'.$boxsize.
'" in atom "'.preg_replace(
'#[^a-zA-Z0-9 _\\-]#',
'?', $atomname).
'" at offset: '.($atom_structure[
'offset'] + $atomoffset);
352 $atom_structure['data'] = null;
353 $atomoffset = strlen($atom_data);
354 break;
355 }
356 $atomoffset += $boxsize;
357
358 switch ($boxtype) {
359 case 'mean':
360 case 'name':
361 $atom_structure[$boxtype] = substr($boxdata, 4);
362 break;
363
364 case 'data':
367 switch ($atom_structure['flags_raw']) {
368 case 0:
369 case 21:
370 switch ($atomname) {
371 case 'cpil':
372 case 'hdvd':
373 case 'pcst':
374 case 'pgap':
375
377 break;
378
379 case 'tmpo':
380
382 break;
383
384 case 'disk':
385 case 'trkn':
386
389 $atom_structure['data'] = empty($num) ? '' : $num;
390 $atom_structure['data'] .= empty($num_total) ? '' : '/'.$num_total;
391 break;
392
393 case 'gnre':
394
397 break;
398
399 case 'rtng':
400
403 break;
404
405 case 'stik':
406
409 break;
410
411 case 'sfID':
412
415 break;
416
417 case 'egid':
418 case 'purl':
419 $atom_structure['data'] = substr($boxdata, 8);
420 break;
421
422 case 'plID':
423
425
426 case 'atID':
427 case 'cnID':
428 case 'geID':
429 case 'tves':
430 case 'tvsn':
431 default:
432
434 }
435 break;
436
437 case 1:
438 case 13:
439 default:
440 $atom_structure['data'] = substr($boxdata, 8);
441 if ($atomname == 'covr') {
442
443 if (preg_match('#^\xFF\xD8\xFF#', $atom_structure['data'])) {
444 $atom_structure['image_mime'] = 'image/jpeg';
445 } elseif (preg_match('#^\x89\x50\x4E\x47\x0D\x0A\x1A\x0A#', $atom_structure['data'])) {
446 $atom_structure['image_mime'] = 'image/png';
447 } elseif (preg_match('#^GIF#', $atom_structure['data'])) {
448 $atom_structure['image_mime'] = 'image/gif';
449 }
450 }
451 break;
452
453 }
454 break;
455
456 default:
457 $info[
'warning'][] =
'Unknown QuickTime box type: "'.preg_replace(
'#[^a-zA-Z0-9 _\\-]#',
'?', $boxtype).
'" ('.trim(
getid3_lib::PrintHexBytes($boxtype)).
') at offset '.$baseoffset;
458 $atom_structure['data'] = $atom_data;
459
460 }
461 }
462 }
463 }
465 break;
466
467
468 case 'play':
470
471 $info[
'quicktime'][
'autoplay'] = $atom_structure[
'autoplay'];
472 break;
473
474
475 case 'WLOC':
478 break;
479
480
481 case 'LOOP':
482 case 'SelO':
483 case 'AllF':
485 break;
486
487
488 case 'name':
489 case 'MCPS':
490 case '@PRM':
491 case '@PRQ':
492 $atom_structure['data'] = $atom_data;
493 break;
494
495
496 case 'cmvd':
497
498
500
501 $CompressedFileData = substr($atom_data, 4);
502 if ($UncompressedHeader = @gzuncompress($CompressedFileData)) {
504 } else {
505 $info[
'warning'][] =
'Error decompressing compressed MOV atom at offset '.$atom_structure[
'offset'];
506 }
507 break;
508
509
510 case 'dcom':
511 $atom_structure['compression_id'] = $atom_data;
513 break;
514
515
516 case 'rdrf':
519 $atom_structure['flags']['internal_data'] = (bool) ($atom_structure['flags_raw'] & 0x000001);
520
521 $atom_structure['reference_type_name'] = substr($atom_data, 4, 4);
523 switch ($atom_structure['reference_type_name']) {
524 case 'url ':
525 $atom_structure[
'url'] = $this->
NoNullString(substr($atom_data, 12));
526 break;
527
528 case 'alis':
529 $atom_structure['file_alias'] = substr($atom_data, 12);
530 break;
531
532 case 'rsrc':
533 $atom_structure['resource_alias'] = substr($atom_data, 12);
534 break;
535
536 default:
537 $atom_structure['data'] = substr($atom_data, 12);
538 break;
539 }
540 break;
541
542
543 case 'rmqu':
545 break;
546
547
548 case 'rmcs':
552 break;
553
554
555 case 'rmvc':
558 $atom_structure['gestalt_selector'] = substr($atom_data, 4, 4);
562 break;
563
564
565 case 'rmcd':
568 $atom_structure['component_type'] = substr($atom_data, 4, 4);
569 $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
570 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
574 break;
575
576
577 case 'rmdr':
581
582 $atom_structure['data_rate_bps'] = $atom_structure['data_rate'] * 10;
583 break;
584
585
586 case 'rmla':
590
592 if (empty(
$info[
'comments'][
'language']) || (!in_array($atom_structure[
'language'],
$info[
'comments'][
'language']))) {
593 $info[
'comments'][
'language'][] = $atom_structure[
'language'];
594 }
595 break;
596
597
598 case 'rmla':
602 break;
603
604
605 case 'ptv ':
606
612
613 $atom_structure['flags']['play_on_open'] = (bool) $atom_structure['play_on_open_flag'];
614 $atom_structure['flags']['slide_show'] = (bool) $atom_structure['slide_show_flag'];
615
616 $ptv_lookup[0] = 'normal';
617 $ptv_lookup[1] = 'double';
618 $ptv_lookup[2] = 'half';
619 $ptv_lookup[3] = 'full';
620 $ptv_lookup[4] = 'current';
621 if (isset($ptv_lookup[$atom_structure['display_size_raw']])) {
622 $atom_structure['display_size'] = $ptv_lookup[$atom_structure['display_size_raw']];
623 } else {
624 $info[
'warning'][] =
'unknown "ptv " display constant ('.$atom_structure[
'display_size_raw'].
')';
625 }
626 break;
627
628
629 case 'stsd':
633 $stsdEntriesDataOffset = 8;
634 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
635 $atom_structure[
'sample_description_table'][$i][
'size'] =
getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 4));
636 $stsdEntriesDataOffset += 4;
637 $atom_structure['sample_description_table'][$i]['data_format'] = substr($atom_data, $stsdEntriesDataOffset, 4);
638 $stsdEntriesDataOffset += 4;
639 $atom_structure[
'sample_description_table'][$i][
'reserved'] =
getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 6));
640 $stsdEntriesDataOffset += 6;
641 $atom_structure[
'sample_description_table'][$i][
'reference_index'] =
getid3_lib::BigEndian2Int(substr($atom_data, $stsdEntriesDataOffset, 2));
642 $stsdEntriesDataOffset += 2;
643 $atom_structure['sample_description_table'][$i]['data'] = substr($atom_data, $stsdEntriesDataOffset, ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2));
644 $stsdEntriesDataOffset += ($atom_structure['sample_description_table'][$i]['size'] - 4 - 4 - 6 - 2);
645
646 $atom_structure[
'sample_description_table'][$i][
'encoder_version'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 0, 2));
647 $atom_structure[
'sample_description_table'][$i][
'encoder_revision'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 2, 2));
648 $atom_structure['sample_description_table'][$i]['encoder_vendor'] = substr($atom_structure['sample_description_table'][$i]['data'], 4, 4);
649
650 switch ($atom_structure['sample_description_table'][$i]['encoder_vendor']) {
651
652 case "\x00\x00\x00\x00":
653
654 $atom_structure[
'sample_description_table'][$i][
'audio_channels'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 8, 2));
655 $atom_structure[
'sample_description_table'][$i][
'audio_bit_depth'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 10, 2));
656 $atom_structure[
'sample_description_table'][$i][
'audio_compression_id'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 12, 2));
657 $atom_structure[
'sample_description_table'][$i][
'audio_packet_size'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 14, 2));
658 $atom_structure[
'sample_description_table'][$i][
'audio_sample_rate'] =
getid3_lib::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 16, 4));
659
660
661
662 $atom_structure[
'sample_description_table'][$i][
'temporal_quality'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 8, 4));
663 $atom_structure[
'sample_description_table'][$i][
'spatial_quality'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 12, 4));
664 $atom_structure[
'sample_description_table'][$i][
'width'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 16, 2));
665 $atom_structure[
'sample_description_table'][$i][
'height'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 18, 2));
666 $atom_structure[
'sample_description_table'][$i][
'resolution_x'] =
getid3_lib::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 24, 4));
667 $atom_structure[
'sample_description_table'][$i][
'resolution_y'] =
getid3_lib::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 28, 4));
668 $atom_structure[
'sample_description_table'][$i][
'data_size'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 32, 4));
669 $atom_structure[
'sample_description_table'][$i][
'frame_count'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 36, 2));
670 $atom_structure['sample_description_table'][$i]['compressor_name'] = substr($atom_structure['sample_description_table'][$i]['data'], 38, 4);
671 $atom_structure[
'sample_description_table'][$i][
'pixel_depth'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 42, 2));
672 $atom_structure[
'sample_description_table'][$i][
'color_table_id'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 44, 2));
673
674 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
675 case '2vuY':
676 case 'avc1':
677 case 'cvid':
678 case 'dvc ':
679 case 'dvcp':
680 case 'gif ':
681 case 'h263':
682 case 'jpeg':
683 case 'kpcd':
684 case 'mjpa':
685 case 'mjpb':
686 case 'mp4v':
687 case 'png ':
688 case 'raw ':
689 case 'rle ':
690 case 'rpza':
691 case 'smc ':
692 case 'SVQ1':
693 case 'SVQ3':
694 case 'tiff':
695 case 'v210':
696 case 'v216':
697 case 'v308':
698 case 'v408':
699 case 'v410':
700 case 'yuv2':
701 $info[
'fileformat'] =
'mp4';
702 $info[
'video'][
'fourcc'] = $atom_structure[
'sample_description_table'][$i][
'data_format'];
703
704
705if (!empty($atom_structure['sample_description_table'][$i]['width']) && !empty($atom_structure['sample_description_table'][$i]['height'])) {
706
707 $info[
'video'][
'resolution_x'] = $atom_structure[
'sample_description_table'][$i][
'width'];
708 $info[
'video'][
'resolution_y'] = $atom_structure[
'sample_description_table'][$i][
'height'];
709 $info[
'quicktime'][
'video'][
'resolution_x'] =
$info[
'video'][
'resolution_x'];
710 $info[
'quicktime'][
'video'][
'resolution_y'] =
$info[
'video'][
'resolution_y'];
711}
712 break;
713
714 case 'qtvr':
715 $info[
'video'][
'dataformat'] =
'quicktimevr';
716 break;
717
718 case 'mp4a':
719 default:
721 $info[
'quicktime'][
'audio'][
'sample_rate'] = $atom_structure[
'sample_description_table'][$i][
'audio_sample_rate'];
722 $info[
'quicktime'][
'audio'][
'channels'] = $atom_structure[
'sample_description_table'][$i][
'audio_channels'];
723 $info[
'quicktime'][
'audio'][
'bit_depth'] = $atom_structure[
'sample_description_table'][$i][
'audio_bit_depth'];
724 $info[
'audio'][
'codec'] =
$info[
'quicktime'][
'audio'][
'codec'];
725 $info[
'audio'][
'sample_rate'] =
$info[
'quicktime'][
'audio'][
'sample_rate'];
726 $info[
'audio'][
'channels'] =
$info[
'quicktime'][
'audio'][
'channels'];
727 $info[
'audio'][
'bits_per_sample'] =
$info[
'quicktime'][
'audio'][
'bit_depth'];
728 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
729 case 'raw ':
730 case 'alac':
731 $info[
'audio'][
'lossless'] =
true;
732 break;
733 default:
734 $info[
'audio'][
'lossless'] =
false;
735 break;
736 }
737 break;
738 }
739 break;
740
741 default:
742 switch ($atom_structure['sample_description_table'][$i]['data_format']) {
743 case 'mp4s':
744 $info[
'fileformat'] =
'mp4';
745 break;
746
747 default:
748
749 $atom_structure[
'sample_description_table'][$i][
'video_temporal_quality'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 8, 4));
750 $atom_structure[
'sample_description_table'][$i][
'video_spatial_quality'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 12, 4));
751 $atom_structure[
'sample_description_table'][$i][
'video_frame_width'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 16, 2));
752 $atom_structure[
'sample_description_table'][$i][
'video_frame_height'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 18, 2));
753 $atom_structure[
'sample_description_table'][$i][
'video_resolution_x'] =
getid3_lib::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 20, 4));
754 $atom_structure[
'sample_description_table'][$i][
'video_resolution_y'] =
getid3_lib::FixedPoint16_16(substr($atom_structure[
'sample_description_table'][$i][
'data'], 24, 4));
755 $atom_structure[
'sample_description_table'][$i][
'video_data_size'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 28, 4));
756 $atom_structure[
'sample_description_table'][$i][
'video_frame_count'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 32, 2));
757 $atom_structure[
'sample_description_table'][$i][
'video_encoder_name_len'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 34, 1));
758 $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']);
759 $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_depth'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 66, 2));
760 $atom_structure[
'sample_description_table'][$i][
'video_color_table_id'] =
getid3_lib::BigEndian2Int(substr($atom_structure[
'sample_description_table'][$i][
'data'], 68, 2));
761
762 $atom_structure['sample_description_table'][$i]['video_pixel_color_type'] = (($atom_structure['sample_description_table'][$i]['video_pixel_color_depth'] > 32) ? 'grayscale' : 'color');
763 $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_name'] = $this->
QuicktimeColorNameLookup($atom_structure[
'sample_description_table'][$i][
'video_pixel_color_depth']);
764
765 if ($atom_structure['sample_description_table'][$i]['video_pixel_color_name'] != 'invalid') {
766 $info[
'quicktime'][
'video'][
'codec_fourcc'] = $atom_structure[
'sample_description_table'][$i][
'data_format'];
767 $info[
'quicktime'][
'video'][
'codec_fourcc_lookup'] = $this->
QuicktimeVideoCodecLookup($atom_structure[
'sample_description_table'][$i][
'data_format']);
768 $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']);
769 $info[
'quicktime'][
'video'][
'color_depth'] = $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_depth'];
770 $info[
'quicktime'][
'video'][
'color_depth_name'] = $atom_structure[
'sample_description_table'][$i][
'video_pixel_color_name'];
771
772 $info[
'video'][
'codec'] =
$info[
'quicktime'][
'video'][
'codec'];
773 $info[
'video'][
'bits_per_sample'] =
$info[
'quicktime'][
'video'][
'color_depth'];
774 }
775 $info[
'video'][
'lossless'] =
false;
776 $info[
'video'][
'pixel_aspect_ratio'] = (float) 1;
777 break;
778 }
779 break;
780 }
781 switch (strtolower($atom_structure['sample_description_table'][$i]['data_format'])) {
782 case 'mp4a':
783 $info[
'audio'][
'dataformat'] =
'mp4';
784 $info[
'quicktime'][
'audio'][
'codec'] =
'mp4';
785 break;
786
787 case '3ivx':
788 case '3iv1':
789 case '3iv2':
790 $info[
'video'][
'dataformat'] =
'3ivx';
791 break;
792
793 case 'xvid':
794 $info[
'video'][
'dataformat'] =
'xvid';
795 break;
796
797 case 'mp4v':
798 $info[
'video'][
'dataformat'] =
'mpeg4';
799 break;
800
801 case 'divx':
802 case 'div1':
803 case 'div2':
804 case 'div3':
805 case 'div4':
806 case 'div5':
807 case 'div6':
808 $info[
'video'][
'dataformat'] =
'divx';
809 break;
810
811 default:
812
813 break;
814 }
815 unset($atom_structure['sample_description_table'][$i]['data']);
816 }
817 break;
818
819
820 case 'stts':
824 $sttsEntriesDataOffset = 8;
825
826 $frames_count = 0;
827
828 $max_stts_entries_to_scan = (
$info[
'php_memory_limit'] ? min(floor($this->getid3->memory_limit / 10000), $atom_structure[
'number_entries']) : $atom_structure[
'number_entries']);
829 if ($max_stts_entries_to_scan < $atom_structure['number_entries']) {
830 $info[
'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).';
831 }
832 for ($i = 0; $i < $max_stts_entries_to_scan; $i++) {
833 $atom_structure[
'time_to_sample_table'][$i][
'sample_count'] =
getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
834 $sttsEntriesDataOffset += 4;
835 $atom_structure[
'time_to_sample_table'][$i][
'sample_duration'] =
getid3_lib::BigEndian2Int(substr($atom_data, $sttsEntriesDataOffset, 4));
836 $sttsEntriesDataOffset += 4;
837
838 $frames_count += $atom_structure['time_to_sample_table'][$i]['sample_count'];
839
840
841
842
843
844
845
846
847
848
849
850 }
851 $info[
'quicktime'][
'stts_framecount'][] = $frames_count;
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869 break;
870
871
872 case 'stss':
877 $stssEntriesDataOffset = 8;
878 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
880 $stssEntriesDataOffset += 4;
881 }
882 }
883 break;
884
885
886 case 'stsc':
891 $stscEntriesDataOffset = 8;
892 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
893 $atom_structure[
'sample_to_chunk_table'][$i][
'first_chunk'] =
getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
894 $stscEntriesDataOffset += 4;
895 $atom_structure[
'sample_to_chunk_table'][$i][
'samples_per_chunk'] =
getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
896 $stscEntriesDataOffset += 4;
897 $atom_structure[
'sample_to_chunk_table'][$i][
'sample_description'] =
getid3_lib::BigEndian2Int(substr($atom_data, $stscEntriesDataOffset, 4));
898 $stscEntriesDataOffset += 4;
899 }
900 }
901 break;
902
903
904 case 'stsz':
910 $stszEntriesDataOffset = 12;
911 if ($atom_structure['sample_size'] == 0) {
912 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
914 $stszEntriesDataOffset += 4;
915 }
916 }
917 }
918 break;
919
920
921 case 'stco':
926 $stcoEntriesDataOffset = 8;
927 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
929 $stcoEntriesDataOffset += 4;
930 }
931 }
932 break;
933
934
935 case 'co64':
940 $stcoEntriesDataOffset = 8;
941 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
943 $stcoEntriesDataOffset += 8;
944 }
945 }
946 break;
947
948
949 case 'dref':
953 $drefDataOffset = 8;
954 for ($i = 0; $i < $atom_structure['number_entries']; $i++) {
956 $drefDataOffset += 4;
957 $atom_structure['data_references'][$i]['type'] = substr($atom_data, $drefDataOffset, 4);
958 $drefDataOffset += 4;
960 $drefDataOffset += 1;
962 $drefDataOffset += 3;
963 $atom_structure['data_references'][$i]['data'] = substr($atom_data, $drefDataOffset, ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3));
964 $drefDataOffset += ($atom_structure['data_references'][$i]['size'] - 4 - 4 - 1 - 3);
965
966 $atom_structure['data_references'][$i]['flags']['self_reference'] = (bool) ($atom_structure['data_references'][$i]['flags_raw'] & 0x001);
967 }
968 break;
969
970
971 case 'gmin':
980 break;
981
982
983 case 'smhd':
988 break;
989
990
991 case 'vmhd':
998
999 $atom_structure['flags']['no_lean_ahead'] = (bool) ($atom_structure['flags_raw'] & 0x001);
1000 break;
1001
1002
1003 case 'hdlr':
1006 $atom_structure['component_type'] = substr($atom_data, 4, 4);
1007 $atom_structure['component_subtype'] = substr($atom_data, 8, 4);
1008 $atom_structure['component_manufacturer'] = substr($atom_data, 12, 4);
1011 $atom_structure[
'component_name'] = $this->
Pascal2String(substr($atom_data, 24));
1012
1013 if (($atom_structure['component_subtype'] == 'STpn') && ($atom_structure['component_manufacturer'] == 'zzzz')) {
1014 $info[
'video'][
'dataformat'] =
'quicktimevr';
1015 }
1016 break;
1017
1018
1019 case 'mdhd':
1028
1029 if ($atom_structure['time_scale'] == 0) {
1030 $info[
'error'][] =
'Corrupt Quicktime file: mdhd.time_scale == zero';
1031 return false;
1032 }
1033 $info[
'quicktime'][
'time_scale'] = (isset(
$info[
'quicktime'][
'time_scale']) ? max(
$info[
'quicktime'][
'time_scale'], $atom_structure[
'time_scale']) : $atom_structure[
'time_scale']);
1034
1037 $atom_structure['playtime_seconds'] = $atom_structure['duration'] / $atom_structure['time_scale'];
1039 if (empty(
$info[
'comments'][
'language']) || (!in_array($atom_structure[
'language'],
$info[
'comments'][
'language']))) {
1040 $info[
'comments'][
'language'][] = $atom_structure[
'language'];
1041 }
1042 break;
1043
1044
1045 case 'pnot':
1048 $atom_structure['atom_type'] = substr($atom_data, 6, 4);
1050
1052 break;
1053
1054
1055 case 'crgn':
1058 $atom_structure['clipping_data'] = substr($atom_data, 10);
1059 break;
1060
1061
1062 case 'load':
1067
1068 $atom_structure['default_hints']['double_buffer'] = (bool) ($atom_structure['default_hints_raw'] & 0x0020);
1069 $atom_structure['default_hints']['high_quality'] = (bool) ($atom_structure['default_hints_raw'] & 0x0100);
1070 break;
1071
1072
1073 case 'tmcd':
1074 case 'chap':
1075 case 'sync':
1076 case 'scpt':
1077 case 'ssrc':
1078 for ($i = 0; $i < strlen($atom_data); $i += 4) {
1080 }
1081 break;
1082
1083
1084 case 'elst':
1088 for ($i = 0; $i < $atom_structure['number_entries']; $i++ ) {
1089 $atom_structure[
'edit_list'][$i][
'track_duration'] =
getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($i * 12) + 0, 4));
1092 }
1093 break;
1094
1095
1096 case 'kmat':
1099 $atom_structure['matte_data_raw'] = substr($atom_data, 4);
1100 break;
1101
1102
1103 case 'ctab':
1107 for ($colortableentry = 0; $colortableentry < $atom_structure['color_table_size']; $colortableentry++) {
1108 $atom_structure[
'color_table'][$colortableentry][
'alpha'] =
getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 0, 2));
1109 $atom_structure[
'color_table'][$colortableentry][
'red'] =
getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 2, 2));
1110 $atom_structure[
'color_table'][$colortableentry][
'green'] =
getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 4, 2));
1111 $atom_structure[
'color_table'][$colortableentry][
'blue'] =
getid3_lib::BigEndian2Int(substr($atom_data, 8 + ($colortableentry * 8) + 6, 2));
1112 }
1113 break;
1114
1115
1116 case 'mvhd':
1125 $atom_structure['reserved'] = substr($atom_data, 26, 10);
1142
1143 if ($atom_structure['time_scale'] == 0) {
1144 $info[
'error'][] =
'Corrupt Quicktime file: mvhd.time_scale == zero';
1145 return false;
1146 }
1149 $info[
'quicktime'][
'time_scale'] = (isset(
$info[
'quicktime'][
'time_scale']) ? max(
$info[
'quicktime'][
'time_scale'], $atom_structure[
'time_scale']) : $atom_structure[
'time_scale']);
1150 $info[
'quicktime'][
'display_scale'] = $atom_structure[
'matrix_a'];
1151 $info[
'playtime_seconds'] = $atom_structure[
'duration'] / $atom_structure[
'time_scale'];
1152 break;
1153
1154
1155 case 'tkhd':
1168
1169
1181 $atom_structure['flags']['enabled'] = (bool) ($atom_structure['flags_raw'] & 0x0001);
1182 $atom_structure['flags']['in_movie'] = (bool) ($atom_structure['flags_raw'] & 0x0002);
1183 $atom_structure['flags']['in_preview'] = (bool) ($atom_structure['flags_raw'] & 0x0004);
1184 $atom_structure['flags']['in_poster'] = (bool) ($atom_structure['flags_raw'] & 0x0008);
1187
1188 if ($atom_structure['flags']['enabled'] == 1) {
1189 if (!isset(
$info[
'video'][
'resolution_x']) || !isset(
$info[
'video'][
'resolution_y'])) {
1190 $info[
'video'][
'resolution_x'] = $atom_structure[
'width'];
1191 $info[
'video'][
'resolution_y'] = $atom_structure[
'height'];
1192 }
1193 $info[
'video'][
'resolution_x'] = max(
$info[
'video'][
'resolution_x'], $atom_structure[
'width']);
1194 $info[
'video'][
'resolution_y'] = max(
$info[
'video'][
'resolution_y'], $atom_structure[
'height']);
1195 $info[
'quicktime'][
'video'][
'resolution_x'] =
$info[
'video'][
'resolution_x'];
1196 $info[
'quicktime'][
'video'][
'resolution_y'] =
$info[
'video'][
'resolution_y'];
1197 } else {
1198
1199
1200
1201
1202 }
1203 break;
1204
1205
1206 case 'iods':
1207
1208
1209 $offset = 0;
1211 $offset += 1;
1213 $offset += 3;
1215 $offset += 1;
1217
1219 $offset += 2;
1221 $offset += 1;
1223 $offset += 1;
1225 $offset += 1;
1227 $offset += 1;
1229 $offset += 1;
1230
1231 $atom_structure['num_iods_tracks'] = ($atom_structure['length'] - 7) / 6;
1232 for ($i = 0; $i < $atom_structure['num_iods_tracks']; $i++) {
1234 $offset += 1;
1236
1238 $offset += 4;
1239 }
1240
1243 break;
1244
1245 case 'ftyp':
1246 $atom_structure['signature'] = substr($atom_data, 0, 4);
1248 $atom_structure['fourcc'] = substr($atom_data, 8, 4);
1249 break;
1250
1251 case 'mdat':
1252
1253
1254
1255
1256
1257 $mdat_offset = 0;
1258 while (true) {
1259 if (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x08".'wide') {
1260 $mdat_offset += 8;
1261 } elseif (substr($atom_data, $mdat_offset, 8) == "\x00\x00\x00\x00".'mdat') {
1262 $mdat_offset += 8;
1263 } else {
1264 break;
1265 }
1266 }
1267
1268
1270 && ($chapter_string_length < 1000)
1271 && ($chapter_string_length <= (strlen($atom_data) - $mdat_offset - 2))
1272 && preg_match('#^[\x20-\xFF]+$#', substr($atom_data, $mdat_offset + 2, $chapter_string_length), $chapter_matches)) {
1273 $mdat_offset += (2 + $chapter_string_length);
1274 @
$info[
'quicktime'][
'comments'][
'chapters'][] = $chapter_matches[0];
1275 }
1276
1277
1278
1279 if (($atomsize > 8) && (!isset(
$info[
'avdataend_tmp']) || (
$info[
'quicktime'][$atomname][
'size'] > (
$info[
'avdataend_tmp'] -
$info[
'avdataoffset'])))) {
1280
1281 $info[
'avdataoffset'] = $atom_structure[
'offset'] + 8;
1282 $OldAVDataEnd =
$info[
'avdataend'];
1283 $info[
'avdataend'] = $atom_structure[
'offset'] + $atom_structure[
'size'];
1284
1285 $getid3_temp =
new getID3();
1286 $getid3_temp->openfile($this->getid3->filename);
1287 $getid3_temp->info[
'avdataoffset'] =
$info[
'avdataoffset'];
1288 $getid3_temp->info[
'avdataend'] =
$info[
'avdataend'];
1290 if ($getid3_mp3->MPEGaudioHeaderValid($getid3_mp3->MPEGaudioHeaderDecode($this->fread(4)))) {
1291 $getid3_mp3->getOnlyMPEGaudioInfo($getid3_temp->info['avdataoffset'], false);
1292 if (!empty($getid3_temp->info['warning'])) {
1293 foreach ($getid3_temp->info['warning'] as $value) {
1294 $info[
'warning'][] = $value;
1295 }
1296 }
1297 if (!empty($getid3_temp->info['mpeg'])) {
1298 $info[
'mpeg'] = $getid3_temp->info[
'mpeg'];
1299 if (isset(
$info[
'mpeg'][
'audio'])) {
1300 $info[
'audio'][
'dataformat'] =
'mp3';
1301 $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')));
1302 $info[
'audio'][
'sample_rate'] =
$info[
'mpeg'][
'audio'][
'sample_rate'];
1303 $info[
'audio'][
'channels'] =
$info[
'mpeg'][
'audio'][
'channels'];
1304 $info[
'audio'][
'bitrate'] =
$info[
'mpeg'][
'audio'][
'bitrate'];
1305 $info[
'audio'][
'bitrate_mode'] = strtolower(
$info[
'mpeg'][
'audio'][
'bitrate_mode']);
1306 $info[
'bitrate'] =
$info[
'audio'][
'bitrate'];
1307 }
1308 }
1309 }
1310 unset($getid3_mp3, $getid3_temp);
1311 $info[
'avdataend'] = $OldAVDataEnd;
1312 unset($OldAVDataEnd);
1313
1314 }
1315
1316 unset($mdat_offset, $chapter_string_length, $chapter_matches);
1317 break;
1318
1319 case 'free':
1320 case 'skip':
1321 case 'wide':
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331 break;
1332
1333
1334 case 'nsav':
1335
1337 break;
1338
1339 case 'ctyp':
1340
1341
1342
1343
1344 $atom_structure['ctyp'] = substr($atom_data, 0, 4);
1345 $info[
'quicktime'][
'controller'] = $atom_structure[
'ctyp'];
1346 switch ($atom_structure['ctyp']) {
1347 case 'qtvr':
1348 $info[
'video'][
'dataformat'] =
'quicktimevr';
1349 break;
1350 }
1351 break;
1352
1353 case 'pano':
1355 break;
1356
1357 case 'hint':
1358 case 'hinf':
1359 case 'hinv':
1360 case 'hnti':
1361 $info[
'quicktime'][
'hinting'] =
true;
1362 break;
1363
1364 case 'imgt':
1365 for ($i = 0; $i < ($atom_structure['size'] - 8); $i += 4) {
1367 }
1368 break;
1369
1370
1371
1372 case 'FXTC':
1373 case 'PrmA':
1374 case 'code':
1375 case 'FIEL':
1376 case 'tapt':
1377
1378
1379
1380 case 'ctts':
1381 case 'cslg':
1382 case 'sdtp':
1383 case 'stps':
1384
1385 break;
1386
1387 case "\xA9".'xyz':
1388 $atom_structure['data'] = $atom_data;
1389 if (preg_match('#([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)([\\+\\-][0-9\\.]+)?/$#i', $atom_data, $matches)) {
1390 @list($all, $latitude, $longitude, $altitude) = $matches;
1391 $info[
'quicktime'][
'comments'][
'gps_latitude'][] = floatval($latitude);
1392 $info[
'quicktime'][
'comments'][
'gps_longitude'][] = floatval($longitude);
1393 if (!empty($altitude)) {
1394 $info[
'quicktime'][
'comments'][
'gps_altitude'][] = floatval($altitude);
1395 }
1396 } else {
1397 $info[
'warning'][] =
'QuickTime atom "©xyz" data does not match expected data pattern at offset '.$baseoffset.
'. Please report as getID3() bug.';
1398 }
1399 break;
1400
1401 case 'NCDT':
1402
1403
1405 break;
1406 case 'NCTH':
1407 case 'NCVW':
1408
1409 if (preg_match('/^\xFF\xD8\xFF/', $atom_data)) {
1410 $atom_structure['data'] = $atom_data;
1411 $atom_structure['image_mime'] = 'image/jpeg';
1412 $atom_structure['description'] = (($atomname == 'NCTH') ? 'Nikon Camera Thumbnail Image' : (($atomname == 'NCVW') ? 'Nikon Camera Preview Image' : 'Nikon preview image'));
1413 $info[
'quicktime'][
'comments'][
'picture'][] = array(
'image_mime'=>$atom_structure[
'image_mime'],
'data'=>$atom_data,
'description'=>$atom_structure[
'description']);
1414 }
1415 break;
1416 case 'NCTG':
1418 break;
1419 case 'NCHD':
1420 case 'NCDB':
1421 case 'CNCV':
1422 $atom_structure['data'] = $atom_data;
1423 break;
1424
1425 case "\x00\x00\x00\x00":
1426 case 'meta':
1427
1428
1429
1430
1434
1435 break;
1436
1437 case 'data':
1438
1439 $atom_structure['language'] = substr($atom_data, 4 + 0, 2);
1441 $atom_structure['data'] = substr($atom_data, 4 + 4);
1442 break;
1443
1444 default:
1445 $info[
'warning'][] =
'Unknown QuickTime atom type: "'.preg_replace(
'#[^a-zA-Z0-9 _\\-]#',
'?', $atomname).
'" ('.trim(
getid3_lib::PrintHexBytes($atomname)).
') at offset '.$baseoffset;
1446 $atom_structure['data'] = $atom_data;
1447 break;
1448 }
1449 array_pop($atomHierarchy);
1450 return $atom_structure;
1451 }
LookupGenreName($genreid, $allowSCMPXextended=true)
FixedPoint16_16($rawdata)
PrintHexBytes($string, $hex=true, $spaces=true, $htmlsafe=true)
QuicktimeParseNikonNCTG($atom_data)
QuicktimeVideoCodecLookup($codecid)
QuicktimeIODSvideoProfileName($video_profile_id)
QuicktimeContentRatingLookup($rtng)
NoNullString($nullterminatedstring)
quicktime_read_mp4_descr_length($data, &$offset)
QuicktimeAudioCodecLookup($codecid)
QuicktimeColorNameLookup($colordepthid)
QuicktimeSTIKLookup($stik)
QuicktimeIODSaudioProfileName($audio_profile_id)
QuicktimeLanguageLookup($languageid)
CopyToAppropriateCommentsSection($keyname, $data, &$ThisFileInfo)
QuicktimeDCOMLookup($compressionid)
Pascal2String($pascalstring)
QuicktimeStoreFrontCodeLookup($sfid)
QuicktimeParseContainerAtom($atomdata, &$ThisFileInfo, $baseoffset, &$atomHierarchy, $ParseAllPossibleAtoms)