1 <?php |
1 <?php |
2 ///////////////////////////////////////////////////////////////// |
2 ///////////////////////////////////////////////////////////////// |
3 /// getID3() by James Heinrich <info@getid3.org> // |
3 /// getID3() by James Heinrich <info@getid3.org> // |
4 // available at http://getid3.sourceforge.net // |
4 // available at https://github.com/JamesHeinrich/getID3 // |
5 // or http://www.getid3.org // |
5 // or https://www.getid3.org // |
6 // also https://github.com/JamesHeinrich/getID3 // |
6 // or http://getid3.sourceforge.net // |
|
7 // see readme.txt for more details // |
|
8 ///////////////////////////////////////////////////////////////// |
|
9 // // |
|
10 // module.audio-video.flv.php // |
|
11 // module for analyzing Shockwave Flash Video files // |
|
12 // dependencies: NONE // |
|
13 // // |
|
14 ///////////////////////////////////////////////////////////////// |
7 // // |
15 // // |
8 // FLV module by Seth Kaufman <sethØwhirl-i-gig*com> // |
16 // FLV module by Seth Kaufman <sethØwhirl-i-gig*com> // |
9 // // |
17 // // |
10 // * version 0.1 (26 June 2005) // |
18 // * version 0.1 (26 June 2005) // |
11 // // |
|
12 // // |
19 // // |
13 // * version 0.1.1 (15 July 2005) // |
20 // * version 0.1.1 (15 July 2005) // |
14 // minor modifications by James Heinrich <info@getid3.org> // |
21 // minor modifications by James Heinrich <info@getid3.org> // |
15 // // |
22 // // |
16 // * version 0.2 (22 February 2006) // |
23 // * version 0.2 (22 February 2006) // |
41 // // |
48 // // |
42 // * version 0.7.0 (16 Jul 2013) // |
49 // * version 0.7.0 (16 Jul 2013) // |
43 // handle GETID3_FLV_VIDEO_VP6FLV_ALPHA // |
50 // handle GETID3_FLV_VIDEO_VP6FLV_ALPHA // |
44 // improved AVCSequenceParameterSetReader::readData() // |
51 // improved AVCSequenceParameterSetReader::readData() // |
45 // by Xander Schouwerwou <schouwerwouØgmail*com> // |
52 // by Xander Schouwerwou <schouwerwouØgmail*com> // |
46 // // |
|
47 ///////////////////////////////////////////////////////////////// |
|
48 // // |
|
49 // module.audio-video.flv.php // |
|
50 // module for analyzing Shockwave Flash Video files // |
|
51 // dependencies: NONE // |
|
52 // /// |
53 // /// |
53 ///////////////////////////////////////////////////////////////// |
54 ///////////////////////////////////////////////////////////////// |
|
55 |
|
56 if (!defined('GETID3_INCLUDEPATH')) { // prevent path-exposing attacks that access modules directly on public webservers |
|
57 exit; |
|
58 } |
54 |
59 |
55 define('GETID3_FLV_TAG_AUDIO', 8); |
60 define('GETID3_FLV_TAG_AUDIO', 8); |
56 define('GETID3_FLV_TAG_VIDEO', 9); |
61 define('GETID3_FLV_TAG_VIDEO', 9); |
57 define('GETID3_FLV_TAG_META', 18); |
62 define('GETID3_FLV_TAG_META', 18); |
58 |
63 |
71 define('H264_PROFILE_HIGH10', 110); |
76 define('H264_PROFILE_HIGH10', 110); |
72 define('H264_PROFILE_HIGH422', 122); |
77 define('H264_PROFILE_HIGH422', 122); |
73 define('H264_PROFILE_HIGH444', 144); |
78 define('H264_PROFILE_HIGH444', 144); |
74 define('H264_PROFILE_HIGH444_PREDICTIVE', 244); |
79 define('H264_PROFILE_HIGH444_PREDICTIVE', 244); |
75 |
80 |
76 class getid3_flv extends getid3_handler { |
81 class getid3_flv extends getid3_handler |
77 |
82 { |
78 const magic = 'FLV'; |
83 const magic = 'FLV'; |
79 |
84 |
80 public $max_frames = 100000; // break out of the loop if too many frames have been scanned; only scan this many if meta frame does not contain useful duration |
85 /** |
81 |
86 * Break out of the loop if too many frames have been scanned; only scan this |
|
87 * many if meta frame does not contain useful duration. |
|
88 * |
|
89 * @var int |
|
90 */ |
|
91 public $max_frames = 100000; |
|
92 |
|
93 /** |
|
94 * @return bool |
|
95 */ |
82 public function Analyze() { |
96 public function Analyze() { |
83 $info = &$this->getid3->info; |
97 $info = &$this->getid3->info; |
84 |
98 |
85 $this->fseek($info['avdataoffset']); |
99 $this->fseek($info['avdataoffset']); |
86 |
100 |
353 15 => 'Device-specific sound', |
371 15 => 'Device-specific sound', |
354 ); |
372 ); |
355 return (isset($lookup[$id]) ? $lookup[$id] : false); |
373 return (isset($lookup[$id]) ? $lookup[$id] : false); |
356 } |
374 } |
357 |
375 |
|
376 /** |
|
377 * @param int $id |
|
378 * |
|
379 * @return int|false |
|
380 */ |
358 public static function audioRateLookup($id) { |
381 public static function audioRateLookup($id) { |
359 static $lookup = array( |
382 static $lookup = array( |
360 0 => 5500, |
383 0 => 5500, |
361 1 => 11025, |
384 1 => 11025, |
362 2 => 22050, |
385 2 => 22050, |
363 3 => 44100, |
386 3 => 44100, |
364 ); |
387 ); |
365 return (isset($lookup[$id]) ? $lookup[$id] : false); |
388 return (isset($lookup[$id]) ? $lookup[$id] : false); |
366 } |
389 } |
367 |
390 |
|
391 /** |
|
392 * @param int $id |
|
393 * |
|
394 * @return int|false |
|
395 */ |
368 public static function audioBitDepthLookup($id) { |
396 public static function audioBitDepthLookup($id) { |
369 static $lookup = array( |
397 static $lookup = array( |
370 0 => 8, |
398 0 => 8, |
371 1 => 16, |
399 1 => 16, |
372 ); |
400 ); |
373 return (isset($lookup[$id]) ? $lookup[$id] : false); |
401 return (isset($lookup[$id]) ? $lookup[$id] : false); |
374 } |
402 } |
375 |
403 |
|
404 /** |
|
405 * @param int $id |
|
406 * |
|
407 * @return string|false |
|
408 */ |
376 public static function videoCodecLookup($id) { |
409 public static function videoCodecLookup($id) { |
377 static $lookup = array( |
410 static $lookup = array( |
378 GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', |
411 GETID3_FLV_VIDEO_H263 => 'Sorenson H.263', |
379 GETID3_FLV_VIDEO_SCREEN => 'Screen video', |
412 GETID3_FLV_VIDEO_SCREEN => 'Screen video', |
380 GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', |
413 GETID3_FLV_VIDEO_VP6FLV => 'On2 VP6', |
384 ); |
417 ); |
385 return (isset($lookup[$id]) ? $lookup[$id] : false); |
418 return (isset($lookup[$id]) ? $lookup[$id] : false); |
386 } |
419 } |
387 } |
420 } |
388 |
421 |
389 class AMFStream { |
422 class AMFStream |
|
423 { |
|
424 /** |
|
425 * @var string |
|
426 */ |
390 public $bytes; |
427 public $bytes; |
|
428 |
|
429 /** |
|
430 * @var int |
|
431 */ |
391 public $pos; |
432 public $pos; |
392 |
433 |
|
434 /** |
|
435 * @param string $bytes |
|
436 */ |
393 public function __construct(&$bytes) { |
437 public function __construct(&$bytes) { |
394 $this->bytes =& $bytes; |
438 $this->bytes =& $bytes; |
395 $this->pos = 0; |
439 $this->pos = 0; |
396 } |
440 } |
397 |
441 |
398 public function readByte() { |
442 /** |
399 return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1)); |
443 * @return int |
400 } |
444 */ |
401 |
445 public function readByte() { // 8-bit |
402 public function readInt() { |
446 return ord(substr($this->bytes, $this->pos++, 1)); |
|
447 } |
|
448 |
|
449 /** |
|
450 * @return int |
|
451 */ |
|
452 public function readInt() { // 16-bit |
403 return ($this->readByte() << 8) + $this->readByte(); |
453 return ($this->readByte() << 8) + $this->readByte(); |
404 } |
454 } |
405 |
455 |
406 public function readLong() { |
456 /** |
|
457 * @return int |
|
458 */ |
|
459 public function readLong() { // 32-bit |
407 return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); |
460 return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte(); |
408 } |
461 } |
409 |
462 |
|
463 /** |
|
464 * @return float|false |
|
465 */ |
410 public function readDouble() { |
466 public function readDouble() { |
411 return getid3_lib::BigEndian2Float($this->read(8)); |
467 return getid3_lib::BigEndian2Float($this->read(8)); |
412 } |
468 } |
413 |
469 |
|
470 /** |
|
471 * @return string |
|
472 */ |
414 public function readUTF() { |
473 public function readUTF() { |
415 $length = $this->readInt(); |
474 $length = $this->readInt(); |
416 return $this->read($length); |
475 return $this->read($length); |
417 } |
476 } |
418 |
477 |
|
478 /** |
|
479 * @return string |
|
480 */ |
419 public function readLongUTF() { |
481 public function readLongUTF() { |
420 $length = $this->readLong(); |
482 $length = $this->readLong(); |
421 return $this->read($length); |
483 return $this->read($length); |
422 } |
484 } |
423 |
485 |
|
486 /** |
|
487 * @param int $length |
|
488 * |
|
489 * @return string |
|
490 */ |
424 public function read($length) { |
491 public function read($length) { |
425 $val = substr($this->bytes, $this->pos, $length); |
492 $val = substr($this->bytes, $this->pos, $length); |
426 $this->pos += $length; |
493 $this->pos += $length; |
427 return $val; |
494 return $val; |
428 } |
495 } |
429 |
496 |
|
497 /** |
|
498 * @return int |
|
499 */ |
430 public function peekByte() { |
500 public function peekByte() { |
431 $pos = $this->pos; |
501 $pos = $this->pos; |
432 $val = $this->readByte(); |
502 $val = $this->readByte(); |
433 $this->pos = $pos; |
503 $this->pos = $pos; |
434 return $val; |
504 return $val; |
435 } |
505 } |
436 |
506 |
|
507 /** |
|
508 * @return int |
|
509 */ |
437 public function peekInt() { |
510 public function peekInt() { |
438 $pos = $this->pos; |
511 $pos = $this->pos; |
439 $val = $this->readInt(); |
512 $val = $this->readInt(); |
440 $this->pos = $pos; |
513 $this->pos = $pos; |
441 return $val; |
514 return $val; |
442 } |
515 } |
443 |
516 |
|
517 /** |
|
518 * @return int |
|
519 */ |
444 public function peekLong() { |
520 public function peekLong() { |
445 $pos = $this->pos; |
521 $pos = $this->pos; |
446 $val = $this->readLong(); |
522 $val = $this->readLong(); |
447 $this->pos = $pos; |
523 $this->pos = $pos; |
448 return $val; |
524 return $val; |
449 } |
525 } |
450 |
526 |
|
527 /** |
|
528 * @return float|false |
|
529 */ |
451 public function peekDouble() { |
530 public function peekDouble() { |
452 $pos = $this->pos; |
531 $pos = $this->pos; |
453 $val = $this->readDouble(); |
532 $val = $this->readDouble(); |
454 $this->pos = $pos; |
533 $this->pos = $pos; |
455 return $val; |
534 return $val; |
456 } |
535 } |
457 |
536 |
|
537 /** |
|
538 * @return string |
|
539 */ |
458 public function peekUTF() { |
540 public function peekUTF() { |
459 $pos = $this->pos; |
541 $pos = $this->pos; |
460 $val = $this->readUTF(); |
542 $val = $this->readUTF(); |
461 $this->pos = $pos; |
543 $this->pos = $pos; |
462 return $val; |
544 return $val; |
463 } |
545 } |
464 |
546 |
|
547 /** |
|
548 * @return string |
|
549 */ |
465 public function peekLongUTF() { |
550 public function peekLongUTF() { |
466 $pos = $this->pos; |
551 $pos = $this->pos; |
467 $val = $this->readLongUTF(); |
552 $val = $this->readLongUTF(); |
468 $this->pos = $pos; |
553 $this->pos = $pos; |
469 return $val; |
554 return $val; |
470 } |
555 } |
471 } |
556 } |
472 |
557 |
473 class AMFReader { |
558 class AMFReader |
|
559 { |
|
560 /** |
|
561 * @var AMFStream |
|
562 */ |
474 public $stream; |
563 public $stream; |
475 |
564 |
476 public function __construct(&$stream) { |
565 /** |
477 $this->stream =& $stream; |
566 * @param AMFStream $stream |
478 } |
567 */ |
479 |
568 public function __construct(AMFStream $stream) { |
|
569 $this->stream = $stream; |
|
570 } |
|
571 |
|
572 /** |
|
573 * @return mixed |
|
574 */ |
480 public function readData() { |
575 public function readData() { |
481 $value = null; |
576 $value = null; |
482 |
577 |
483 $type = $this->stream->readByte(); |
578 $type = $this->stream->readByte(); |
484 switch ($type) { |
579 switch ($type) { |
545 } |
639 } |
546 |
640 |
547 return $value; |
641 return $value; |
548 } |
642 } |
549 |
643 |
|
644 /** |
|
645 * @return float|false |
|
646 */ |
550 public function readDouble() { |
647 public function readDouble() { |
551 return $this->stream->readDouble(); |
648 return $this->stream->readDouble(); |
552 } |
649 } |
553 |
650 |
|
651 /** |
|
652 * @return bool |
|
653 */ |
554 public function readBoolean() { |
654 public function readBoolean() { |
555 return $this->stream->readByte() == 1; |
655 return $this->stream->readByte() == 1; |
556 } |
656 } |
557 |
657 |
|
658 /** |
|
659 * @return string |
|
660 */ |
558 public function readString() { |
661 public function readString() { |
559 return $this->stream->readUTF(); |
662 return $this->stream->readUTF(); |
560 } |
663 } |
561 |
664 |
|
665 /** |
|
666 * @return array |
|
667 */ |
562 public function readObject() { |
668 public function readObject() { |
563 // Get highest numerical index - ignored |
669 // Get highest numerical index - ignored |
564 // $highestIndex = $this->stream->readLong(); |
670 // $highestIndex = $this->stream->readLong(); |
565 |
671 |
566 $data = array(); |
672 $data = array(); |
|
673 $key = null; |
567 |
674 |
568 while ($key = $this->stream->readUTF()) { |
675 while ($key = $this->stream->readUTF()) { |
569 $data[$key] = $this->readData(); |
676 $data[$key] = $this->readData(); |
570 } |
677 } |
571 // Mixed array record ends with empty string (0x00 0x00) and 0x09 |
678 // Mixed array record ends with empty string (0x00 0x00) and 0x09 |
574 $this->stream->readByte(); |
681 $this->stream->readByte(); |
575 } |
682 } |
576 return $data; |
683 return $data; |
577 } |
684 } |
578 |
685 |
|
686 /** |
|
687 * @return array |
|
688 */ |
579 public function readMixedArray() { |
689 public function readMixedArray() { |
580 // Get highest numerical index - ignored |
690 // Get highest numerical index - ignored |
581 $highestIndex = $this->stream->readLong(); |
691 $highestIndex = $this->stream->readLong(); |
582 |
692 |
583 $data = array(); |
693 $data = array(); |
|
694 $key = null; |
584 |
695 |
585 while ($key = $this->stream->readUTF()) { |
696 while ($key = $this->stream->readUTF()) { |
586 if (is_numeric($key)) { |
697 if (is_numeric($key)) { |
587 $key = (float) $key; |
698 $key = (int) $key; |
588 } |
699 } |
589 $data[$key] = $this->readData(); |
700 $data[$key] = $this->readData(); |
590 } |
701 } |
591 // Mixed array record ends with empty string (0x00 0x00) and 0x09 |
702 // Mixed array record ends with empty string (0x00 0x00) and 0x09 |
592 if (($key == '') && ($this->stream->peekByte() == 0x09)) { |
703 if (($key == '') && ($this->stream->peekByte() == 0x09)) { |
595 } |
706 } |
596 |
707 |
597 return $data; |
708 return $data; |
598 } |
709 } |
599 |
710 |
|
711 /** |
|
712 * @return array |
|
713 */ |
600 public function readArray() { |
714 public function readArray() { |
601 $length = $this->stream->readLong(); |
715 $length = $this->stream->readLong(); |
602 $data = array(); |
716 $data = array(); |
603 |
717 |
604 for ($i = 0; $i < $length; $i++) { |
718 for ($i = 0; $i < $length; $i++) { |
605 $data[] = $this->readData(); |
719 $data[] = $this->readData(); |
606 } |
720 } |
607 return $data; |
721 return $data; |
608 } |
722 } |
609 |
723 |
|
724 /** |
|
725 * @return float|false |
|
726 */ |
610 public function readDate() { |
727 public function readDate() { |
611 $timestamp = $this->stream->readDouble(); |
728 $timestamp = $this->stream->readDouble(); |
612 $timezone = $this->stream->readInt(); |
729 $timezone = $this->stream->readInt(); |
613 return $timestamp; |
730 return $timestamp; |
614 } |
731 } |
615 |
732 |
|
733 /** |
|
734 * @return string |
|
735 */ |
616 public function readLongString() { |
736 public function readLongString() { |
617 return $this->stream->readLongUTF(); |
737 return $this->stream->readLongUTF(); |
618 } |
738 } |
619 |
739 |
|
740 /** |
|
741 * @return string |
|
742 */ |
620 public function readXML() { |
743 public function readXML() { |
621 return $this->stream->readLongUTF(); |
744 return $this->stream->readLongUTF(); |
622 } |
745 } |
623 |
746 |
|
747 /** |
|
748 * @return array |
|
749 */ |
624 public function readTypedObject() { |
750 public function readTypedObject() { |
625 $className = $this->stream->readUTF(); |
751 $className = $this->stream->readUTF(); |
626 return $this->readObject(); |
752 return $this->readObject(); |
627 } |
753 } |
628 } |
754 } |
629 |
755 |
630 class AVCSequenceParameterSetReader { |
756 class AVCSequenceParameterSetReader |
|
757 { |
|
758 /** |
|
759 * @var string |
|
760 */ |
631 public $sps; |
761 public $sps; |
632 public $start = 0; |
762 public $start = 0; |
633 public $currentBytes = 0; |
763 public $currentBytes = 0; |
634 public $currentBits = 0; |
764 public $currentBits = 0; |
|
765 |
|
766 /** |
|
767 * @var int |
|
768 */ |
635 public $width; |
769 public $width; |
|
770 |
|
771 /** |
|
772 * @var int |
|
773 */ |
636 public $height; |
774 public $height; |
637 |
775 |
|
776 /** |
|
777 * @param string $sps |
|
778 */ |
638 public function __construct($sps) { |
779 public function __construct($sps) { |
639 $this->sps = $sps; |
780 $this->sps = $sps; |
640 } |
781 } |
641 |
782 |
642 public function readData() { |
783 public function readData() { |
689 $this->width = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2); |
830 $this->width = (($pic_width_in_mbs_minus1 + 1) * 16) - ($frame_crop_left_offset * 2) - ($frame_crop_right_offset * 2); |
690 $this->height = ((2 - $frame_mbs_only_flag) * ($pic_height_in_map_units_minus1 + 1) * 16) - ($frame_crop_top_offset * 2) - ($frame_crop_bottom_offset * 2); |
831 $this->height = ((2 - $frame_mbs_only_flag) * ($pic_height_in_map_units_minus1 + 1) * 16) - ($frame_crop_top_offset * 2) - ($frame_crop_bottom_offset * 2); |
691 } |
832 } |
692 } |
833 } |
693 |
834 |
|
835 /** |
|
836 * @param int $bits |
|
837 */ |
694 public function skipBits($bits) { |
838 public function skipBits($bits) { |
695 $newBits = $this->currentBits + $bits; |
839 $newBits = $this->currentBits + $bits; |
696 $this->currentBytes += (int)floor($newBits / 8); |
840 $this->currentBytes += (int)floor($newBits / 8); |
697 $this->currentBits = $newBits % 8; |
841 $this->currentBits = $newBits % 8; |
698 } |
842 } |
699 |
843 |
|
844 /** |
|
845 * @return int |
|
846 */ |
700 public function getBit() { |
847 public function getBit() { |
701 $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01; |
848 $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01; |
702 $this->skipBits(1); |
849 $this->skipBits(1); |
703 return $result; |
850 return $result; |
704 } |
851 } |
705 |
852 |
|
853 /** |
|
854 * @param int $bits |
|
855 * |
|
856 * @return int |
|
857 */ |
706 public function getBits($bits) { |
858 public function getBits($bits) { |
707 $result = 0; |
859 $result = 0; |
708 for ($i = 0; $i < $bits; $i++) { |
860 for ($i = 0; $i < $bits; $i++) { |
709 $result = ($result << 1) + $this->getBit(); |
861 $result = ($result << 1) + $this->getBit(); |
710 } |
862 } |
711 return $result; |
863 return $result; |
712 } |
864 } |
713 |
865 |
|
866 /** |
|
867 * @return int |
|
868 */ |
714 public function expGolombUe() { |
869 public function expGolombUe() { |
715 $significantBits = 0; |
870 $significantBits = 0; |
716 $bit = $this->getBit(); |
871 $bit = $this->getBit(); |
717 while ($bit == 0) { |
872 while ($bit == 0) { |
718 $significantBits++; |
873 $significantBits++; |
724 } |
879 } |
725 } |
880 } |
726 return (1 << $significantBits) + $this->getBits($significantBits) - 1; |
881 return (1 << $significantBits) + $this->getBits($significantBits) - 1; |
727 } |
882 } |
728 |
883 |
|
884 /** |
|
885 * @return int |
|
886 */ |
729 public function expGolombSe() { |
887 public function expGolombSe() { |
730 $result = $this->expGolombUe(); |
888 $result = $this->expGolombUe(); |
731 if (($result & 0x01) == 0) { |
889 if (($result & 0x01) == 0) { |
732 return -($result >> 1); |
890 return -($result >> 1); |
733 } else { |
891 } else { |
734 return ($result + 1) >> 1; |
892 return ($result + 1) >> 1; |
735 } |
893 } |
736 } |
894 } |
737 |
895 |
|
896 /** |
|
897 * @return int |
|
898 */ |
738 public function getWidth() { |
899 public function getWidth() { |
739 return $this->width; |
900 return $this->width; |
740 } |
901 } |
741 |
902 |
|
903 /** |
|
904 * @return int |
|
905 */ |
742 public function getHeight() { |
906 public function getHeight() { |
743 return $this->height; |
907 return $this->height; |
744 } |
908 } |
745 } |
909 } |