home *** CD-ROM | disk | FTP | other *** search
/ HTML Examples / WP.iso / wordpress2 / wp-includes / ID3 / module.audio-video.flv.php < prev    next >
Encoding:
PHP Script  |  2017-07-31  |  24.8 KB  |  746 lines

  1. <?php
  2. /////////////////////////////////////////////////////////////////
  3. /// getID3() by James Heinrich <info@getid3.org>               //
  4. //  available at http://getid3.sourceforge.net                 //
  5. //            or http://www.getid3.org                         //
  6. //          also https://github.com/JamesHeinrich/getID3       //
  7. //                                                             //
  8. //  FLV module by Seth Kaufman <seth├ÿwhirl-i-gig*com>          //
  9. //                                                             //
  10. //  * version 0.1 (26 June 2005)                               //
  11. //                                                             //
  12. //                                                             //
  13. //  * version 0.1.1 (15 July 2005)                             //
  14. //  minor modifications by James Heinrich <info@getid3.org>    //
  15. //                                                             //
  16. //  * version 0.2 (22 February 2006)                           //
  17. //  Support for On2 VP6 codec and meta information             //
  18. //    by Steve Webster <steve.webster├ÿfeaturecreep*com>        //
  19. //                                                             //
  20. //  * version 0.3 (15 June 2006)                               //
  21. //  Modified to not read entire file into memory               //
  22. //    by James Heinrich <info@getid3.org>                      //
  23. //                                                             //
  24. //  * version 0.4 (07 December 2007)                           //
  25. //  Bugfixes for incorrectly parsed FLV dimensions             //
  26. //    and incorrect parsing of onMetaTag                       //
  27. //    by Evgeny Moysevich <moysevich├ÿgmail*com>                //
  28. //                                                             //
  29. //  * version 0.5 (21 May 2009)                                //
  30. //  Fixed parsing of audio tags and added additional codec     //
  31. //    details. The duration is now read from onMetaTag (if     //
  32. //    exists), rather than parsing whole file                  //
  33. //    by Nigel Barnes <ngbarnes├ÿhotmail*com>                   //
  34. //                                                             //
  35. //  * version 0.6 (24 May 2009)                                //
  36. //  Better parsing of files with h264 video                    //
  37. //    by Evgeny Moysevich <moysevich├ÿgmail*com>                //
  38. //                                                             //
  39. //  * version 0.6.1 (30 May 2011)                              //
  40. //    prevent infinite loops in expGolombUe()                  //
  41. //                                                             //
  42. //  * version 0.7.0 (16 Jul 2013)                              //
  43. //  handle GETID3_FLV_VIDEO_VP6FLV_ALPHA                       //
  44. //  improved AVCSequenceParameterSetReader::readData()         //
  45. //    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. /////////////////////////////////////////////////////////////////
  54.  
  55. define('GETID3_FLV_TAG_AUDIO',          8);
  56. define('GETID3_FLV_TAG_VIDEO',          9);
  57. define('GETID3_FLV_TAG_META',          18);
  58.  
  59. define('GETID3_FLV_VIDEO_H263',         2);
  60. define('GETID3_FLV_VIDEO_SCREEN',       3);
  61. define('GETID3_FLV_VIDEO_VP6FLV',       4);
  62. define('GETID3_FLV_VIDEO_VP6FLV_ALPHA', 5);
  63. define('GETID3_FLV_VIDEO_SCREENV2',     6);
  64. define('GETID3_FLV_VIDEO_H264',         7);
  65.  
  66. define('H264_AVC_SEQUENCE_HEADER',          0);
  67. define('H264_PROFILE_BASELINE',            66);
  68. define('H264_PROFILE_MAIN',                77);
  69. define('H264_PROFILE_EXTENDED',            88);
  70. define('H264_PROFILE_HIGH',               100);
  71. define('H264_PROFILE_HIGH10',             110);
  72. define('H264_PROFILE_HIGH422',            122);
  73. define('H264_PROFILE_HIGH444',            144);
  74. define('H264_PROFILE_HIGH444_PREDICTIVE', 244);
  75.  
  76. class getid3_flv extends getid3_handler {
  77.  
  78.     const magic = 'FLV';
  79.  
  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
  81.  
  82.     public function Analyze() {
  83.         $info = &$this->getid3->info;
  84.  
  85.         $this->fseek($info['avdataoffset']);
  86.  
  87.         $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
  88.         $FLVheader = $this->fread(5);
  89.  
  90.         $info['fileformat'] = 'flv';
  91.         $info['flv']['header']['signature'] =                           substr($FLVheader, 0, 3);
  92.         $info['flv']['header']['version']   = getid3_lib::BigEndian2Int(substr($FLVheader, 3, 1));
  93.         $TypeFlags                          = getid3_lib::BigEndian2Int(substr($FLVheader, 4, 1));
  94.  
  95.         if ($info['flv']['header']['signature'] != self::magic) {
  96.             $this->error('Expecting "'.getid3_lib::PrintHexBytes(self::magic).'" at offset '.$info['avdataoffset'].', found "'.getid3_lib::PrintHexBytes($info['flv']['header']['signature']).'"');
  97.             unset($info['flv'], $info['fileformat']);
  98.             return false;
  99.         }
  100.  
  101.         $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x04);
  102.         $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x01);
  103.  
  104.         $FrameSizeDataLength = getid3_lib::BigEndian2Int($this->fread(4));
  105.         $FLVheaderFrameLength = 9;
  106.         if ($FrameSizeDataLength > $FLVheaderFrameLength) {
  107.             $this->fseek($FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
  108.         }
  109.         $Duration = 0;
  110.         $found_video = false;
  111.         $found_audio = false;
  112.         $found_meta  = false;
  113.         $found_valid_meta_playtime = false;
  114.         $tagParseCount = 0;
  115.         $info['flv']['framecount'] = array('total'=>0, 'audio'=>0, 'video'=>0);
  116.         $flv_framecount = &$info['flv']['framecount'];
  117.         while ((($this->ftell() + 16) < $info['avdataend']) && (($tagParseCount++ <= $this->max_frames) || !$found_valid_meta_playtime))  {
  118.             $ThisTagHeader = $this->fread(16);
  119.  
  120.             $PreviousTagLength = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  0, 4));
  121.             $TagType           = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  4, 1));
  122.             $DataLength        = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  5, 3));
  123.             $Timestamp         = getid3_lib::BigEndian2Int(substr($ThisTagHeader,  8, 3));
  124.             $LastHeaderByte    = getid3_lib::BigEndian2Int(substr($ThisTagHeader, 15, 1));
  125.             $NextOffset = $this->ftell() - 1 + $DataLength;
  126.             if ($Timestamp > $Duration) {
  127.                 $Duration = $Timestamp;
  128.             }
  129.  
  130.             $flv_framecount['total']++;
  131.             switch ($TagType) {
  132.                 case GETID3_FLV_TAG_AUDIO:
  133.                     $flv_framecount['audio']++;
  134.                     if (!$found_audio) {
  135.                         $found_audio = true;
  136.                         $info['flv']['audio']['audioFormat']     = ($LastHeaderByte >> 4) & 0x0F;
  137.                         $info['flv']['audio']['audioRate']       = ($LastHeaderByte >> 2) & 0x03;
  138.                         $info['flv']['audio']['audioSampleSize'] = ($LastHeaderByte >> 1) & 0x01;
  139.                         $info['flv']['audio']['audioType']       =  $LastHeaderByte       & 0x01;
  140.                     }
  141.                     break;
  142.  
  143.                 case GETID3_FLV_TAG_VIDEO:
  144.                     $flv_framecount['video']++;
  145.                     if (!$found_video) {
  146.                         $found_video = true;
  147.                         $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x07;
  148.  
  149.                         $FLVvideoHeader = $this->fread(11);
  150.  
  151.                         if ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H264) {
  152.                             // this code block contributed by: moysevich├ÿgmail*com
  153.  
  154.                             $AVCPacketType = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
  155.                             if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
  156.                                 //    read AVCDecoderConfigurationRecord
  157.                                 $configurationVersion       = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  4, 1));
  158.                                 $AVCProfileIndication       = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  5, 1));
  159.                                 $profile_compatibility      = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  6, 1));
  160.                                 $lengthSizeMinusOne         = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  7, 1));
  161.                                 $numOfSequenceParameterSets = getid3_lib::BigEndian2Int(substr($FLVvideoHeader,  8, 1));
  162.  
  163.                                 if (($numOfSequenceParameterSets & 0x1F) != 0) {
  164.                                     //    there is at least one SequenceParameterSet
  165.                                     //    read size of the first SequenceParameterSet
  166.                                     //$spsSize = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
  167.                                     $spsSize = getid3_lib::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
  168.                                     //    read the first SequenceParameterSet
  169.                                     $sps = $this->fread($spsSize);
  170.                                     if (strlen($sps) == $spsSize) {    //    make sure that whole SequenceParameterSet was red
  171.                                         $spsReader = new AVCSequenceParameterSetReader($sps);
  172.                                         $spsReader->readData();
  173.                                         $info['video']['resolution_x'] = $spsReader->getWidth();
  174.                                         $info['video']['resolution_y'] = $spsReader->getHeight();
  175.                                     }
  176.                                 }
  177.                             }
  178.                             // end: moysevich├ÿgmail*com
  179.  
  180.                         } elseif ($info['flv']['video']['videoCodec'] == GETID3_FLV_VIDEO_H263) {
  181.  
  182.                             $PictureSizeType = (getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 3, 2))) >> 7;
  183.                             $PictureSizeType = $PictureSizeType & 0x0007;
  184.                             $info['flv']['header']['videoSizeType'] = $PictureSizeType;
  185.                             switch ($PictureSizeType) {
  186.                                 case 0:
  187.                                     //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
  188.                                     //$PictureSizeEnc <<= 1;
  189.                                     //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
  190.                                     //$PictureSizeEnc = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
  191.                                     //$PictureSizeEnc <<= 1;
  192.                                     //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
  193.  
  194.                                     $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7;
  195.                                     $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7;
  196.                                     $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFF;
  197.                                     $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFF;
  198.                                     break;
  199.  
  200.                                 case 1:
  201.                                     $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7;
  202.                                     $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 7;
  203.                                     $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xFFFF;
  204.                                     $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xFFFF;
  205.                                     break;
  206.  
  207.                                 case 2:
  208.                                     $info['video']['resolution_x'] = 352;
  209.                                     $info['video']['resolution_y'] = 288;
  210.                                     break;
  211.  
  212.                                 case 3:
  213.                                     $info['video']['resolution_x'] = 176;
  214.                                     $info['video']['resolution_y'] = 144;
  215.                                     break;
  216.  
  217.                                 case 4:
  218.                                     $info['video']['resolution_x'] = 128;
  219.                                     $info['video']['resolution_y'] = 96;
  220.                                     break;
  221.  
  222.                                 case 5:
  223.                                     $info['video']['resolution_x'] = 320;
  224.                                     $info['video']['resolution_y'] = 240;
  225.                                     break;
  226.  
  227.                                 case 6:
  228.                                     $info['video']['resolution_x'] = 160;
  229.                                     $info['video']['resolution_y'] = 120;
  230.                                     break;
  231.  
  232.                                 default:
  233.                                     $info['video']['resolution_x'] = 0;
  234.                                     $info['video']['resolution_y'] = 0;
  235.                                     break;
  236.  
  237.                             }
  238.  
  239.                         } elseif ($info['flv']['video']['videoCodec'] ==  GETID3_FLV_VIDEO_VP6FLV_ALPHA) {
  240.  
  241.                             /* contributed by schouwerwou├ÿgmail*com */
  242.                             if (!isset($info['video']['resolution_x'])) { // only when meta data isn't set
  243.                                 $PictureSizeEnc['x'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
  244.                                 $PictureSizeEnc['y'] = getid3_lib::BigEndian2Int(substr($FLVvideoHeader, 7, 2));
  245.                                 $info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xFF) << 3;
  246.                                 $info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xFF) << 3;
  247.                             }
  248.                             /* end schouwerwou├ÿgmail*com */
  249.  
  250.                         }
  251.                         if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) {
  252.                             $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
  253.                         }
  254.                     }
  255.                     break;
  256.  
  257.                 // Meta tag
  258.                 case GETID3_FLV_TAG_META:
  259.                     if (!$found_meta) {
  260.                         $found_meta = true;
  261.                         $this->fseek(-1, SEEK_CUR);
  262.                         $datachunk = $this->fread($DataLength);
  263.                         $AMFstream = new AMFStream($datachunk);
  264.                         $reader = new AMFReader($AMFstream);
  265.                         $eventName = $reader->readData();
  266.                         $info['flv']['meta'][$eventName] = $reader->readData();
  267.                         unset($reader);
  268.  
  269.                         $copykeys = array('framerate'=>'frame_rate', 'width'=>'resolution_x', 'height'=>'resolution_y', 'audiodatarate'=>'bitrate', 'videodatarate'=>'bitrate');
  270.                         foreach ($copykeys as $sourcekey => $destkey) {
  271.                             if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) {
  272.                                 switch ($sourcekey) {
  273.                                     case 'width':
  274.                                     case 'height':
  275.                                         $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
  276.                                         break;
  277.                                     case 'audiodatarate':
  278.                                         $info['audio'][$destkey] = getid3_lib::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
  279.                                         break;
  280.                                     case 'videodatarate':
  281.                                     case 'frame_rate':
  282.                                     default:
  283.                                         $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
  284.                                         break;
  285.                                 }
  286.                             }
  287.                         }
  288.                         if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
  289.                             $found_valid_meta_playtime = true;
  290.                         }
  291.                     }
  292.                     break;
  293.  
  294.                 default:
  295.                     // noop
  296.                     break;
  297.             }
  298.             $this->fseek($NextOffset);
  299.         }
  300.  
  301.         $info['playtime_seconds'] = $Duration / 1000;
  302.         if ($info['playtime_seconds'] > 0) {
  303.             $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
  304.         }
  305.  
  306.         if ($info['flv']['header']['hasAudio']) {
  307.             $info['audio']['codec']           =   self::audioFormatLookup($info['flv']['audio']['audioFormat']);
  308.             $info['audio']['sample_rate']     =     self::audioRateLookup($info['flv']['audio']['audioRate']);
  309.             $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($info['flv']['audio']['audioSampleSize']);
  310.  
  311.             $info['audio']['channels']   =  $info['flv']['audio']['audioType'] + 1; // 0=mono,1=stereo
  312.             $info['audio']['lossless']   = ($info['flv']['audio']['audioFormat'] ? false : true); // 0=uncompressed
  313.             $info['audio']['dataformat'] = 'flv';
  314.         }
  315.         if (!empty($info['flv']['header']['hasVideo'])) {
  316.             $info['video']['codec']      = self::videoCodecLookup($info['flv']['video']['videoCodec']);
  317.             $info['video']['dataformat'] = 'flv';
  318.             $info['video']['lossless']   = false;
  319.         }
  320.  
  321.         // Set information from meta
  322.         if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
  323.             $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration'];
  324.             $info['bitrate'] = (($info['avdataend'] - $info['avdataoffset']) * 8) / $info['playtime_seconds'];
  325.         }
  326.         if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
  327.             $info['audio']['codec'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']);
  328.         }
  329.         if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
  330.             $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']);
  331.         }
  332.         return true;
  333.     }
  334.  
  335.  
  336.     public static function audioFormatLookup($id) {
  337.         static $lookup = array(
  338.             0  => 'Linear PCM, platform endian',
  339.             1  => 'ADPCM',
  340.             2  => 'mp3',
  341.             3  => 'Linear PCM, little endian',
  342.             4  => 'Nellymoser 16kHz mono',
  343.             5  => 'Nellymoser 8kHz mono',
  344.             6  => 'Nellymoser',
  345.             7  => 'G.711A-law logarithmic PCM',
  346.             8  => 'G.711 mu-law logarithmic PCM',
  347.             9  => 'reserved',
  348.             10 => 'AAC',
  349.             11 => 'Speex',
  350.             12 => false, // unknown?
  351.             13 => false, // unknown?
  352.             14 => 'mp3 8kHz',
  353.             15 => 'Device-specific sound',
  354.         );
  355.         return (isset($lookup[$id]) ? $lookup[$id] : false);
  356.     }
  357.  
  358.     public static function audioRateLookup($id) {
  359.         static $lookup = array(
  360.             0 =>  5500,
  361.             1 => 11025,
  362.             2 => 22050,
  363.             3 => 44100,
  364.         );
  365.         return (isset($lookup[$id]) ? $lookup[$id] : false);
  366.     }
  367.  
  368.     public static function audioBitDepthLookup($id) {
  369.         static $lookup = array(
  370.             0 =>  8,
  371.             1 => 16,
  372.         );
  373.         return (isset($lookup[$id]) ? $lookup[$id] : false);
  374.     }
  375.  
  376.     public static function videoCodecLookup($id) {
  377.         static $lookup = array(
  378.             GETID3_FLV_VIDEO_H263         => 'Sorenson H.263',
  379.             GETID3_FLV_VIDEO_SCREEN       => 'Screen video',
  380.             GETID3_FLV_VIDEO_VP6FLV       => 'On2 VP6',
  381.             GETID3_FLV_VIDEO_VP6FLV_ALPHA => 'On2 VP6 with alpha channel',
  382.             GETID3_FLV_VIDEO_SCREENV2     => 'Screen video v2',
  383.             GETID3_FLV_VIDEO_H264         => 'Sorenson H.264',
  384.         );
  385.         return (isset($lookup[$id]) ? $lookup[$id] : false);
  386.     }
  387. }
  388.  
  389. class AMFStream {
  390.     public $bytes;
  391.     public $pos;
  392.  
  393.     public function __construct(&$bytes) {
  394.         $this->bytes =& $bytes;
  395.         $this->pos = 0;
  396.     }
  397.  
  398.     public function readByte() {
  399.         return getid3_lib::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
  400.     }
  401.  
  402.     public function readInt() {
  403.         return ($this->readByte() << 8) + $this->readByte();
  404.     }
  405.  
  406.     public function readLong() {
  407.         return ($this->readByte() << 24) + ($this->readByte() << 16) + ($this->readByte() << 8) + $this->readByte();
  408.     }
  409.  
  410.     public function readDouble() {
  411.         return getid3_lib::BigEndian2Float($this->read(8));
  412.     }
  413.  
  414.     public function readUTF() {
  415.         $length = $this->readInt();
  416.         return $this->read($length);
  417.     }
  418.  
  419.     public function readLongUTF() {
  420.         $length = $this->readLong();
  421.         return $this->read($length);
  422.     }
  423.  
  424.     public function read($length) {
  425.         $val = substr($this->bytes, $this->pos, $length);
  426.         $this->pos += $length;
  427.         return $val;
  428.     }
  429.  
  430.     public function peekByte() {
  431.         $pos = $this->pos;
  432.         $val = $this->readByte();
  433.         $this->pos = $pos;
  434.         return $val;
  435.     }
  436.  
  437.     public function peekInt() {
  438.         $pos = $this->pos;
  439.         $val = $this->readInt();
  440.         $this->pos = $pos;
  441.         return $val;
  442.     }
  443.  
  444.     public function peekLong() {
  445.         $pos = $this->pos;
  446.         $val = $this->readLong();
  447.         $this->pos = $pos;
  448.         return $val;
  449.     }
  450.  
  451.     public function peekDouble() {
  452.         $pos = $this->pos;
  453.         $val = $this->readDouble();
  454.         $this->pos = $pos;
  455.         return $val;
  456.     }
  457.  
  458.     public function peekUTF() {
  459.         $pos = $this->pos;
  460.         $val = $this->readUTF();
  461.         $this->pos = $pos;
  462.         return $val;
  463.     }
  464.  
  465.     public function peekLongUTF() {
  466.         $pos = $this->pos;
  467.         $val = $this->readLongUTF();
  468.         $this->pos = $pos;
  469.         return $val;
  470.     }
  471. }
  472.  
  473. class AMFReader {
  474.     public $stream;
  475.  
  476.     public function __construct(&$stream) {
  477.         $this->stream =& $stream;
  478.     }
  479.  
  480.     public function readData() {
  481.         $value = null;
  482.  
  483.         $type = $this->stream->readByte();
  484.         switch ($type) {
  485.  
  486.             // Double
  487.             case 0:
  488.                 $value = $this->readDouble();
  489.             break;
  490.  
  491.             // Boolean
  492.             case 1:
  493.                 $value = $this->readBoolean();
  494.                 break;
  495.  
  496.             // String
  497.             case 2:
  498.                 $value = $this->readString();
  499.                 break;
  500.  
  501.             // Object
  502.             case 3:
  503.                 $value = $this->readObject();
  504.                 break;
  505.  
  506.             // null
  507.             case 6:
  508.                 return null;
  509.                 break;
  510.  
  511.             // Mixed array
  512.             case 8:
  513.                 $value = $this->readMixedArray();
  514.                 break;
  515.  
  516.             // Array
  517.             case 10:
  518.                 $value = $this->readArray();
  519.                 break;
  520.  
  521.             // Date
  522.             case 11:
  523.                 $value = $this->readDate();
  524.                 break;
  525.  
  526.             // Long string
  527.             case 13:
  528.                 $value = $this->readLongString();
  529.                 break;
  530.  
  531.             // XML (handled as string)
  532.             case 15:
  533.                 $value = $this->readXML();
  534.                 break;
  535.  
  536.             // Typed object (handled as object)
  537.             case 16:
  538.                 $value = $this->readTypedObject();
  539.                 break;
  540.  
  541.             // Long string
  542.             default:
  543.                 $value = '(unknown or unsupported data type)';
  544.                 break;
  545.         }
  546.  
  547.         return $value;
  548.     }
  549.  
  550.     public function readDouble() {
  551.         return $this->stream->readDouble();
  552.     }
  553.  
  554.     public function readBoolean() {
  555.         return $this->stream->readByte() == 1;
  556.     }
  557.  
  558.     public function readString() {
  559.         return $this->stream->readUTF();
  560.     }
  561.  
  562.     public function readObject() {
  563.         // Get highest numerical index - ignored
  564. //        $highestIndex = $this->stream->readLong();
  565.  
  566.         $data = array();
  567.  
  568.         while ($key = $this->stream->readUTF()) {
  569.             $data[$key] = $this->readData();
  570.         }
  571.         // Mixed array record ends with empty string (0x00 0x00) and 0x09
  572.         if (($key == '') && ($this->stream->peekByte() == 0x09)) {
  573.             // Consume byte
  574.             $this->stream->readByte();
  575.         }
  576.         return $data;
  577.     }
  578.  
  579.     public function readMixedArray() {
  580.         // Get highest numerical index - ignored
  581.         $highestIndex = $this->stream->readLong();
  582.  
  583.         $data = array();
  584.  
  585.         while ($key = $this->stream->readUTF()) {
  586.             if (is_numeric($key)) {
  587.                 $key = (float) $key;
  588.             }
  589.             $data[$key] = $this->readData();
  590.         }
  591.         // Mixed array record ends with empty string (0x00 0x00) and 0x09
  592.         if (($key == '') && ($this->stream->peekByte() == 0x09)) {
  593.             // Consume byte
  594.             $this->stream->readByte();
  595.         }
  596.  
  597.         return $data;
  598.     }
  599.  
  600.     public function readArray() {
  601.         $length = $this->stream->readLong();
  602.         $data = array();
  603.  
  604.         for ($i = 0; $i < $length; $i++) {
  605.             $data[] = $this->readData();
  606.         }
  607.         return $data;
  608.     }
  609.  
  610.     public function readDate() {
  611.         $timestamp = $this->stream->readDouble();
  612.         $timezone = $this->stream->readInt();
  613.         return $timestamp;
  614.     }
  615.  
  616.     public function readLongString() {
  617.         return $this->stream->readLongUTF();
  618.     }
  619.  
  620.     public function readXML() {
  621.         return $this->stream->readLongUTF();
  622.     }
  623.  
  624.     public function readTypedObject() {
  625.         $className = $this->stream->readUTF();
  626.         return $this->readObject();
  627.     }
  628. }
  629.  
  630. class AVCSequenceParameterSetReader {
  631.     public $sps;
  632.     public $start = 0;
  633.     public $currentBytes = 0;
  634.     public $currentBits = 0;
  635.     public $width;
  636.     public $height;
  637.  
  638.     public function __construct($sps) {
  639.         $this->sps = $sps;
  640.     }
  641.  
  642.     public function readData() {
  643.         $this->skipBits(8);
  644.         $this->skipBits(8);
  645.         $profile = $this->getBits(8);                               // read profile
  646.         if ($profile > 0) {
  647.             $this->skipBits(8);
  648.             $level_idc = $this->getBits(8);                         // level_idc
  649.             $this->expGolombUe();                                   // seq_parameter_set_id // sps
  650.             $this->expGolombUe();                                   // log2_max_frame_num_minus4
  651.             $picOrderType = $this->expGolombUe();                   // pic_order_cnt_type
  652.             if ($picOrderType == 0) {
  653.                 $this->expGolombUe();                               // log2_max_pic_order_cnt_lsb_minus4
  654.             } elseif ($picOrderType == 1) {
  655.                 $this->skipBits(1);                                 // delta_pic_order_always_zero_flag
  656.                 $this->expGolombSe();                               // offset_for_non_ref_pic
  657.                 $this->expGolombSe();                               // offset_for_top_to_bottom_field
  658.                 $num_ref_frames_in_pic_order_cnt_cycle = $this->expGolombUe(); // num_ref_frames_in_pic_order_cnt_cycle
  659.                 for ($i = 0; $i < $num_ref_frames_in_pic_order_cnt_cycle; $i++) {
  660.                     $this->expGolombSe();                           // offset_for_ref_frame[ i ]
  661.                 }
  662.             }
  663.             $this->expGolombUe();                                   // num_ref_frames
  664.             $this->skipBits(1);                                     // gaps_in_frame_num_value_allowed_flag
  665.             $pic_width_in_mbs_minus1 = $this->expGolombUe();        // pic_width_in_mbs_minus1
  666.             $pic_height_in_map_units_minus1 = $this->expGolombUe(); // pic_height_in_map_units_minus1
  667.  
  668.             $frame_mbs_only_flag = $this->getBits(1);               // frame_mbs_only_flag
  669.             if ($frame_mbs_only_flag == 0) {
  670.                 $this->skipBits(1);                                 // mb_adaptive_frame_field_flag
  671.             }
  672.             $this->skipBits(1);                                     // direct_8x8_inference_flag
  673.             $frame_cropping_flag = $this->getBits(1);               // frame_cropping_flag
  674.  
  675.             $frame_crop_left_offset   = 0;
  676.             $frame_crop_right_offset  = 0;
  677.             $frame_crop_top_offset    = 0;
  678.             $frame_crop_bottom_offset = 0;
  679.  
  680.             if ($frame_cropping_flag) {
  681.                 $frame_crop_left_offset   = $this->expGolombUe();   // frame_crop_left_offset
  682.                 $frame_crop_right_offset  = $this->expGolombUe();   // frame_crop_right_offset
  683.                 $frame_crop_top_offset    = $this->expGolombUe();   // frame_crop_top_offset
  684.                 $frame_crop_bottom_offset = $this->expGolombUe();   // frame_crop_bottom_offset
  685.             }
  686.             $this->skipBits(1);                                     // vui_parameters_present_flag
  687.             // etc
  688.  
  689.             $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);
  691.         }
  692.     }
  693.  
  694.     public function skipBits($bits) {
  695.         $newBits = $this->currentBits + $bits;
  696.         $this->currentBytes += (int)floor($newBits / 8);
  697.         $this->currentBits = $newBits % 8;
  698.     }
  699.  
  700.     public function getBit() {
  701.         $result = (getid3_lib::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> (7 - $this->currentBits)) & 0x01;
  702.         $this->skipBits(1);
  703.         return $result;
  704.     }
  705.  
  706.     public function getBits($bits) {
  707.         $result = 0;
  708.         for ($i = 0; $i < $bits; $i++) {
  709.             $result = ($result << 1) + $this->getBit();
  710.         }
  711.         return $result;
  712.     }
  713.  
  714.     public function expGolombUe() {
  715.         $significantBits = 0;
  716.         $bit = $this->getBit();
  717.         while ($bit == 0) {
  718.             $significantBits++;
  719.             $bit = $this->getBit();
  720.  
  721.             if ($significantBits > 31) {
  722.                 // something is broken, this is an emergency escape to prevent infinite loops
  723.                 return 0;
  724.             }
  725.         }
  726.         return (1 << $significantBits) + $this->getBits($significantBits) - 1;
  727.     }
  728.  
  729.     public function expGolombSe() {
  730.         $result = $this->expGolombUe();
  731.         if (($result & 0x01) == 0) {
  732.             return -($result >> 1);
  733.         } else {
  734.             return ($result + 1) >> 1;
  735.         }
  736.     }
  737.  
  738.     public function getWidth() {
  739.         return $this->width;
  740.     }
  741.  
  742.     public function getHeight() {
  743.         return $this->height;
  744.     }
  745. }
  746.