コード例 #1
0
ファイル: Aa.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $AAheader = fread($this->getid3->fp, 8);
     $magic = "W�u6";
     if (substr($AAheader, 4, 4) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($AAheader, 4, 4)) . '"';
         return false;
     }
     // shortcut
     $info['aa'] = array();
     $thisfile_au =& $info['aa'];
     $info['fileformat'] = 'aa';
     $info['audio']['dataformat'] = 'aa';
     $info['error'][] = 'Audible Audiobook (.aa) parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']';
     return false;
     $info['audio']['bitrate_mode'] = 'cbr';
     // is it?
     $thisfile_au['encoding'] = 'ISO-8859-1';
     $thisfile_au['filesize'] = Helper::BigEndian2Int(substr($AUheader, 0, 4));
     if ($thisfile_au['filesize'] > $info['avdataend'] - $info['avdataoffset']) {
         $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['filesize'] . '" bytes of data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"';
     }
     $info['audio']['bits_per_sample'] = 16;
     // is it?
     $info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
     $info['audio']['channels'] = $thisfile_au['channels'];
     //$info['playtime_seconds'] = 0;
     //$info['audio']['bitrate'] = 0;
     return true;
 }
コード例 #2
0
ファイル: Szip.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $SZIPHeader = fread($this->getid3->fp, 6);
     if (substr($SZIPHeader, 0, 4) != "SZ\n") {
         $info['error'][] = 'Expecting "53 5A 0A 04" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($SZIPHeader, 0, 4)) . '"';
         return false;
     }
     $info['fileformat'] = 'szip';
     $info['szip']['major_version'] = Helper::BigEndian2Int(substr($SZIPHeader, 4, 1));
     $info['szip']['minor_version'] = Helper::BigEndian2Int(substr($SZIPHeader, 5, 1));
     while (!feof($this->getid3->fp)) {
         $NextBlockID = fread($this->getid3->fp, 2);
         switch ($NextBlockID) {
             case 'SZ':
                 // Note that szip files can be concatenated, this has the same effect as
                 // concatenating the files. this also means that global header blocks
                 // might be present between directory/data blocks.
                 fseek($this->getid3->fp, 4, SEEK_CUR);
                 break;
             case 'BH':
                 $BHheaderbytes = Helper::BigEndian2Int(fread($this->getid3->fp, 3));
                 $BHheaderdata = fread($this->getid3->fp, $BHheaderbytes);
                 $BHheaderoffset = 0;
                 while (strpos($BHheaderdata, "", $BHheaderoffset) > 0) {
                     //filename as \0 terminated string  (empty string indicates end)
                     //owner as \0 terminated string (empty is same as last file)
                     //group as \0 terminated string (empty is same as last file)
                     //3 byte filelength in this block
                     //2 byte access flags
                     //4 byte creation time (like in unix)
                     //4 byte modification time (like in unix)
                     //4 byte access time (like in unix)
                     $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['filename']) + 1;
                     $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['owner']) + 1;
                     $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['group']) + 1;
                     $BHdataArray['filelength'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
                     $BHheaderoffset += 3;
                     $BHdataArray['access_flags'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
                     $BHheaderoffset += 2;
                     $BHdataArray['creation_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $BHdataArray['modification_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $BHdataArray['access_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $info['szip']['BH'][] = $BHdataArray;
                 }
                 break;
             default:
                 break 2;
         }
     }
     return true;
 }
コード例 #3
0
ファイル: Au.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $AUheader = fread($this->getid3->fp, 8);
     $magic = '.snd';
     if (substr($AUheader, 0, 4) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" (".snd") at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($AUheader, 0, 4)) . '"';
         return false;
     }
     // shortcut
     $info['au'] = array();
     $thisfile_au =& $info['au'];
     $info['fileformat'] = 'au';
     $info['audio']['dataformat'] = 'au';
     $info['audio']['bitrate_mode'] = 'cbr';
     $thisfile_au['encoding'] = 'ISO-8859-1';
     $thisfile_au['header_length'] = Helper::BigEndian2Int(substr($AUheader, 4, 4));
     $AUheader .= fread($this->getid3->fp, $thisfile_au['header_length'] - 8);
     $info['avdataoffset'] += $thisfile_au['header_length'];
     $thisfile_au['data_size'] = Helper::BigEndian2Int(substr($AUheader, 8, 4));
     $thisfile_au['data_format_id'] = Helper::BigEndian2Int(substr($AUheader, 12, 4));
     $thisfile_au['sample_rate'] = Helper::BigEndian2Int(substr($AUheader, 16, 4));
     $thisfile_au['channels'] = Helper::BigEndian2Int(substr($AUheader, 20, 4));
     $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
     $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
     $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
     if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
         $info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
     } else {
         unset($thisfile_au['bits_per_sample']);
     }
     $info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
     $info['audio']['channels'] = $thisfile_au['channels'];
     if ($info['avdataoffset'] + $thisfile_au['data_size'] > $info['avdataend']) {
         $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['data_size'] . '" bytes of audio data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"';
     }
     $info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
     $info['audio']['bitrate'] = $thisfile_au['data_size'] * 8 / $info['playtime_seconds'];
     return true;
 }
コード例 #4
0
ファイル: Lpac.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $LPACheader = fread($this->getid3->fp, 14);
     if (substr($LPACheader, 0, 4) != 'LPAC') {
         $info['error'][] = 'Expected "LPAC" at offset ' . $info['avdataoffset'] . ', found "' . $StreamMarker . '"';
         return false;
     }
     $info['avdataoffset'] += 14;
     $info['fileformat'] = 'lpac';
     $info['audio']['dataformat'] = 'lpac';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['lpac']['file_version'] = Helper::BigEndian2Int(substr($LPACheader, 4, 1));
     $flags['audio_type'] = Helper::BigEndian2Int(substr($LPACheader, 5, 1));
     $info['lpac']['total_samples'] = Helper::BigEndian2Int(substr($LPACheader, 6, 4));
     $flags['parameters'] = Helper::BigEndian2Int(substr($LPACheader, 10, 4));
     $info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
     $info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x4);
     $info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x2);
     $info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x1);
     if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) {
         $info['warning'][] = '24-bit and 16-bit flags cannot both be set';
     }
     $info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
     $info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x8000000);
     $info['lpac']['block_length'] = pow(2, ($flags['parameters'] & 0x7000000) >> 24) * 256;
     $info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x800000);
     $info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x400000);
     $info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x40000);
     $info['lpac']['quantization'] = ($flags['parameters'] & 0x1f00) >> 8;
     $info['lpac']['max_prediction_order'] = $flags['parameters'] & 0x3f;
     if ($info['lpac']['flags']['fast_compress'] && $info['lpac']['max_prediction_order'] != 3) {
         $info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "' . $info['lpac']['max_prediction_order'] . '"';
     }
     switch ($info['lpac']['file_version']) {
         case 6:
             if ($info['lpac']['flags']['adaptive_quantization']) {
                 $info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
             }
             if ($info['lpac']['quantization'] != 20) {
                 $info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually ' . $info['lpac']['flags']['Q'];
             }
             break;
         default:
             //$info['warning'][] = 'This version of GetId3Core() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org';
             break;
     }
     $getid3_temp = new GetId3Core();
     $getid3_temp->openfile($this->getid3->filename);
     $getid3_temp->info = $info;
     $getid3_riff = new Riff($getid3_temp);
     $getid3_riff->analyze();
     $info['avdataoffset'] = $getid3_temp->info['avdataoffset'];
     $info['riff'] = $getid3_temp->info['riff'];
     $info['error'] = $getid3_temp->info['error'];
     $info['warning'] = $getid3_temp->info['warning'];
     $info['lpac']['comments']['comment'] = $getid3_temp->info['comments'];
     $info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate'];
     unset($getid3_temp, $getid3_riff);
     $info['audio']['channels'] = $info['lpac']['flags']['stereo'] ? 2 : 1;
     if ($info['lpac']['flags']['24_bit']) {
         $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
     } elseif ($info['lpac']['flags']['16_bit']) {
         $info['audio']['bits_per_sample'] = 16;
     } else {
         $info['audio']['bits_per_sample'] = 8;
     }
     if ($info['lpac']['flags']['fast_compress']) {
         // fast
         $info['audio']['encoder_options'] = '-1';
     } else {
         switch ($info['lpac']['max_prediction_order']) {
             case 20:
                 // simple
                 $info['audio']['encoder_options'] = '-2';
                 break;
             case 30:
                 // medium
                 $info['audio']['encoder_options'] = '-3';
                 break;
             case 40:
                 // high
                 $info['audio']['encoder_options'] = '-4';
                 break;
             case 60:
                 // extrahigh
                 $info['audio']['encoder_options'] = '-5';
                 break;
         }
     }
     $info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate'];
     $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     return true;
 }
コード例 #5
0
ファイル: Mpeg.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     if ($info['avdataend'] <= $info['avdataoffset']) {
         $info['error'][] = '"avdataend" (' . $info['avdataend'] . ') is unexpectedly less-than-or-equal-to "avdataoffset" (' . $info['avdataoffset'] . ')';
         return false;
     }
     $info['fileformat'] = 'mpeg';
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $MPEGstreamData = fread($this->getid3->fp, min(100000, $info['avdataend'] - $info['avdataoffset']));
     $MPEGstreamDataLength = strlen($MPEGstreamData);
     $foundVideo = true;
     $VideoChunkOffset = 0;
     while (substr($MPEGstreamData, $VideoChunkOffset++, 4) !== self::GETID3_MPEG_VIDEO_SEQUENCE_HEADER) {
         if ($VideoChunkOffset >= $MPEGstreamDataLength) {
             $foundVideo = false;
             break;
         }
     }
     if ($foundVideo) {
         // Start code                       32 bits
         // horizontal frame size            12 bits
         // vertical frame size              12 bits
         // pixel aspect ratio                4 bits
         // frame rate                        4 bits
         // bitrate                          18 bits
         // marker bit                        1 bit
         // VBV buffer size                  10 bits
         // constrained parameter flag        1 bit
         // intra quant. matrix flag          1 bit
         // intra quant. matrix values      512 bits (present if matrix flag == 1)
         // non-intra quant. matrix flag      1 bit
         // non-intra quant. matrix values  512 bits (present if matrix flag == 1)
         $info['video']['dataformat'] = 'mpeg';
         $VideoChunkOffset += strlen(self::GETID3_MPEG_VIDEO_SEQUENCE_HEADER) - 1;
         $FrameSizeDWORD = Helper::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 3));
         $VideoChunkOffset += 3;
         $AspectRatioFrameRateDWORD = Helper::BigEndian2Int(substr($MPEGstreamData, $VideoChunkOffset, 1));
         $VideoChunkOffset += 1;
         $assortedinformation = Helper::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 4));
         $VideoChunkOffset += 4;
         $info['mpeg']['video']['raw']['framesize_horizontal'] = ($FrameSizeDWORD & 0xfff000) >> 12;
         // 12 bits for horizontal frame size
         $info['mpeg']['video']['raw']['framesize_vertical'] = $FrameSizeDWORD & 0xfff;
         // 12 bits for vertical frame size
         $info['mpeg']['video']['raw']['pixel_aspect_ratio'] = ($AspectRatioFrameRateDWORD & 0xf0) >> 4;
         $info['mpeg']['video']['raw']['frame_rate'] = $AspectRatioFrameRateDWORD & 0xf;
         $info['mpeg']['video']['framesize_horizontal'] = $info['mpeg']['video']['raw']['framesize_horizontal'];
         $info['mpeg']['video']['framesize_vertical'] = $info['mpeg']['video']['raw']['framesize_vertical'];
         $info['mpeg']['video']['pixel_aspect_ratio'] = $this->MPEGvideoAspectRatioLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']);
         $info['mpeg']['video']['pixel_aspect_ratio_text'] = $this->MPEGvideoAspectRatioTextLookup($info['mpeg']['video']['raw']['pixel_aspect_ratio']);
         $info['mpeg']['video']['frame_rate'] = $this->MPEGvideoFramerateLookup($info['mpeg']['video']['raw']['frame_rate']);
         $info['mpeg']['video']['raw']['bitrate'] = Helper::Bin2Dec(substr($assortedinformation, 0, 18));
         $info['mpeg']['video']['raw']['marker_bit'] = (bool) Helper::Bin2Dec(substr($assortedinformation, 18, 1));
         $info['mpeg']['video']['raw']['vbv_buffer_size'] = Helper::Bin2Dec(substr($assortedinformation, 19, 10));
         $info['mpeg']['video']['raw']['constrained_param_flag'] = (bool) Helper::Bin2Dec(substr($assortedinformation, 29, 1));
         $info['mpeg']['video']['raw']['intra_quant_flag'] = (bool) Helper::Bin2Dec(substr($assortedinformation, 30, 1));
         if ($info['mpeg']['video']['raw']['intra_quant_flag']) {
             // read 512 bits
             $info['mpeg']['video']['raw']['intra_quant'] = Helper::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64));
             $VideoChunkOffset += 64;
             $info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) Helper::Bin2Dec(substr($info['mpeg']['video']['raw']['intra_quant'], 511, 1));
             $info['mpeg']['video']['raw']['intra_quant'] = Helper::Bin2Dec(substr($assortedinformation, 31, 1)) . substr(Helper::BigEndian2Bin(substr($MPEGstreamData, $VideoChunkOffset, 64)), 0, 511);
             if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) {
                 $info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
                 $VideoChunkOffset += 64;
             }
         } else {
             $info['mpeg']['video']['raw']['non_intra_quant_flag'] = (bool) Helper::Bin2Dec(substr($assortedinformation, 31, 1));
             if ($info['mpeg']['video']['raw']['non_intra_quant_flag']) {
                 $info['mpeg']['video']['raw']['non_intra_quant'] = substr($MPEGstreamData, $VideoChunkOffset, 64);
                 $VideoChunkOffset += 64;
             }
         }
         if ($info['mpeg']['video']['raw']['bitrate'] == 0x3ffff) {
             // 18 set bits
             $info['warning'][] = 'This version of GetId3Core() [' . $this->getid3->version() . '] cannot determine average bitrate of VBR MPEG video files';
             $info['mpeg']['video']['bitrate_mode'] = 'vbr';
         } else {
             $info['mpeg']['video']['bitrate'] = $info['mpeg']['video']['raw']['bitrate'] * 400;
             $info['mpeg']['video']['bitrate_mode'] = 'cbr';
             $info['video']['bitrate'] = $info['mpeg']['video']['bitrate'];
         }
         $info['video']['resolution_x'] = $info['mpeg']['video']['framesize_horizontal'];
         $info['video']['resolution_y'] = $info['mpeg']['video']['framesize_vertical'];
         $info['video']['frame_rate'] = $info['mpeg']['video']['frame_rate'];
         $info['video']['bitrate_mode'] = $info['mpeg']['video']['bitrate_mode'];
         $info['video']['pixel_aspect_ratio'] = $info['mpeg']['video']['pixel_aspect_ratio'];
         $info['video']['lossless'] = false;
         $info['video']['bits_per_sample'] = 24;
     } else {
         $info['error'][] = 'Could not find start of video block in the first 100,000 bytes (or before end of file) - this might not be an MPEG-video file?';
     }
     //0x000001B3 begins the sequence_header of every MPEG video stream.
     //But in MPEG-2, this header must immediately be followed by an
     //extension_start_code (0x000001B5) with a sequence_extension ID (1).
     //(This extension contains all the additional MPEG-2 stuff.)
     //MPEG-1 doesn't have this extension, so that's a sure way to tell the
     //difference between MPEG-1 and MPEG-2 video streams.
     if (substr($MPEGstreamData, $VideoChunkOffset, 4) == self::GETID3_MPEG_VIDEO_EXTENSION_START) {
         $info['video']['codec'] = 'MPEG-2';
     } else {
         $info['video']['codec'] = 'MPEG-1';
     }
     $AudioChunkOffset = 0;
     while (true) {
         while (substr($MPEGstreamData, $AudioChunkOffset++, 4) !== self::GETID3_MPEG_AUDIO_START) {
             if ($AudioChunkOffset >= $MPEGstreamDataLength) {
                 break 2;
             }
         }
         $getid3_temp = new GetId3Core();
         $getid3_temp->openfile($this->getid3->filename);
         $getid3_temp->info = $info;
         $getid3_mp3 = new Mp3($getid3_temp);
         for ($i = 0; $i <= 7; ++$i) {
             // some files have the MPEG-audio header 8 bytes after the end of the $00 $00 $01 $C0 signature, some have it up to 13 bytes (or more?) after
             // I have no idea why or what the difference is, so this is a stupid hack.
             // If anybody has any better idea of what's going on, please let me know - info@getid3.org
             fseek($getid3_temp->fp, ftell($this->getid3->fp), SEEK_SET);
             $getid3_temp->info = $info;
             // only overwrite real data if valid header found
             if ($getid3_mp3->decodeMPEGaudioHeader($AudioChunkOffset + 3 + 8 + $i, $getid3_temp->info, false)) {
                 $info = $getid3_temp->info;
                 $info['audio']['bitrate_mode'] = 'cbr';
                 $info['audio']['lossless'] = false;
                 unset($getid3_temp, $getid3_mp3);
                 break 2;
             }
         }
         unset($getid3_temp, $getid3_mp3);
     }
     // Temporary hack to account for interleaving overhead:
     if (!empty($info['video']['bitrate']) && !empty($info['audio']['bitrate'])) {
         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / ($info['video']['bitrate'] + $info['audio']['bitrate']);
         // Interleaved MPEG audio/video files have a certain amount of overhead that varies
         // by both video and audio bitrates, and not in any sensible, linear/logarithmic patter
         // Use interpolated lookup tables to approximately guess how much is overhead, because
         // playtime is calculated as filesize / total-bitrate
         $info['playtime_seconds'] *= $this->MPEGsystemNonOverheadPercentage($info['video']['bitrate'], $info['audio']['bitrate']);
         //switch ($info['video']['bitrate']) {
         //	case('5000000'):
         //		$multiplier = 0.93292642112380355828048824319889;
         //		break;
         //	case('5500000'):
         //		$multiplier = 0.93582895375200989965359777343219;
         //		break;
         //	case('6000000'):
         //		$multiplier = 0.93796247714820932532911373859139;
         //		break;
         //	case('7000000'):
         //		$multiplier = 0.9413264083635103463010117778776;
         //		break;
         //	default:
         //		$multiplier = 1;
         //		break;
         //}
         //$info['playtime_seconds'] *= $multiplier;
         //$info['warning'][] = 'Interleaved MPEG audio/video playtime may be inaccurate. With current hack should be within a few seconds of accurate. Report to info@getid3.org if off by more than 10 seconds.';
         if ($info['video']['bitrate'] < 50000) {
             $info['warning'][] = 'Interleaved MPEG audio/video playtime may be slightly inaccurate for video bitrates below 100kbps. Except in extreme low-bitrate situations, error should be less than 1%. Report to info@getid3.org if greater than this.';
         }
     }
     return true;
 }
コード例 #6
0
ファイル: AMFStream.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return type
  */
 public function readByte()
 {
     return Helper::BigEndian2Int(substr($this->bytes, $this->pos++, 1));
 }
コード例 #7
0
ファイル: Quicktime.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type $atom_data
  *
  * @return type
  *
  * @link http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
  */
 public function QuicktimeParseNikonNCTG($atom_data)
 {
     // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
     // Data is stored as records of:
     // * 4 bytes record type
     // * 2 bytes size of data field type:
     //     0x0001 = flag   (size field *= 1-byte)
     //     0x0002 = char   (size field *= 1-byte)
     //     0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
     //     0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
     //     0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
     //     0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
     //     0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
     // * 2 bytes data size field
     // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
     // all integers are stored BigEndian
     $NCTGtagName = array(0x1 => 'Make', 0x2 => 'Model', 0x3 => 'Software', 0x11 => 'CreateDate', 0x12 => 'DateTimeOriginal', 0x13 => 'FrameCount', 0x16 => 'FrameRate', 0x22 => 'FrameWidth', 0x23 => 'FrameHeight', 0x32 => 'AudioChannels', 0x33 => 'AudioBitsPerSample', 0x34 => 'AudioSampleRate', 0x2000001 => 'MakerNoteVersion', 0x2000005 => 'WhiteBalance', 0x200000b => 'WhiteBalanceFineTune', 0x200001e => 'ColorSpace', 0x2000023 => 'PictureControlData', 0x2000024 => 'WorldTime', 0x2000032 => 'UnknownInfo', 0x2000083 => 'LensType', 0x2000084 => 'Lens');
     $offset = 0;
     $datalength = strlen($atom_data);
     $parsed = array();
     while ($offset < $datalength) {
         //echo GetId3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
         $record_type = Helper::BigEndian2Int(substr($atom_data, $offset, 4));
         $offset += 4;
         $data_size_type = Helper::BigEndian2Int(substr($atom_data, $offset, 2));
         $offset += 2;
         $data_size = Helper::BigEndian2Int(substr($atom_data, $offset, 2));
         $offset += 2;
         switch ($data_size_type) {
             case 0x1:
                 // 0x0001 = flag   (size field *= 1-byte)
                 $data = Helper::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
                 $offset += $data_size * 1;
                 break;
             case 0x2:
                 // 0x0002 = char   (size field *= 1-byte)
                 $data = substr($atom_data, $offset, $data_size * 1);
                 $offset += $data_size * 1;
                 $data = rtrim($data, "");
                 break;
             case 0x3:
                 // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
                 $data = '';
                 for ($i = $data_size - 1; $i >= 0; --$i) {
                     $data .= substr($atom_data, $offset + $i * 2, 2);
                 }
                 $data = Helper::BigEndian2Int($data);
                 $offset += $data_size * 2;
                 break;
             case 0x4:
                 // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
                 $data = '';
                 for ($i = $data_size - 1; $i >= 0; --$i) {
                     $data .= substr($atom_data, $offset + $i * 4, 4);
                 }
                 $data = Helper::BigEndian2Int($data);
                 $offset += $data_size * 4;
                 break;
             case 0x5:
                 // 0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
                 $data = array();
                 for ($i = 0; $i < $data_size; ++$i) {
                     $numerator = Helper::BigEndian2Int(substr($atom_data, $offset + $i * 8 + 0, 4));
                     $denomninator = Helper::BigEndian2Int(substr($atom_data, $offset + $i * 8 + 4, 4));
                     if ($denomninator == 0) {
                         $data[$i] = false;
                     } else {
                         $data[$i] = (double) $numerator / $denomninator;
                     }
                 }
                 $offset += 8 * $data_size;
                 if (count($data) == 1) {
                     $data = $data[0];
                 }
                 break;
             case 0x7:
                 // 0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
                 $data = substr($atom_data, $offset, $data_size * 1);
                 $offset += $data_size * 1;
                 break;
             case 0x8:
                 // 0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
                 $data = substr($atom_data, $offset, $data_size * 2);
                 $offset += $data_size * 2;
                 break;
             default:
                 echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: ' . $data_size_type . '<br>';
                 break 2;
         }
         switch ($record_type) {
             case 0x11:
                 // CreateDate
             // CreateDate
             case 0x12:
                 // DateTimeOriginal
                 $data = strtotime($data);
                 break;
             case 0x200001e:
                 // ColorSpace
                 switch ($data) {
                     case 1:
                         $data = 'sRGB';
                         break;
                     case 2:
                         $data = 'Adobe RGB';
                         break;
                 }
                 break;
             case 0x2000023:
                 // PictureControlData
                 $PictureControlAdjust = array(0 => 'default', 1 => 'quick', 2 => 'full');
                 $FilterEffect = array(0x80 => 'off', 0x81 => 'yellow', 0x82 => 'orange', 0x83 => 'red', 0x84 => 'green', 0xff => 'n/a');
                 $ToningEffect = array(0x80 => 'b&w', 0x81 => 'sepia', 0x82 => 'cyanotype', 0x83 => 'red', 0x84 => 'yellow', 0x85 => 'green', 0x86 => 'blue-green', 0x87 => 'blue', 0x88 => 'purple-blue', 0x89 => 'red-purple', 0xff => 'n/a');
                 $data = array('PictureControlVersion' => substr($data, 0, 4), 'PictureControlName' => rtrim(substr($data, 4, 20), ""), 'PictureControlBase' => rtrim(substr($data, 24, 20), ""), 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))], 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)), 'Sharpness' => ord(substr($data, 50, 1)), 'Contrast' => ord(substr($data, 51, 1)), 'Brightness' => ord(substr($data, 52, 1)), 'Saturation' => ord(substr($data, 53, 1)), 'HueAdjustment' => ord(substr($data, 54, 1)), 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))], 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))], 'ToningSaturation' => ord(substr($data, 57, 1)));
                 break;
             case 0x2000024:
                 // WorldTime
                 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
                 // timezone is stored as offset from GMT in minutes
                 $timezone = Helper::BigEndian2Int(substr($data, 0, 2));
                 if ($timezone & 0x8000) {
                     $timezone = 0 - (0x10000 - $timezone);
                 }
                 $timezone /= 60;
                 $dst = (bool) Helper::BigEndian2Int(substr($data, 2, 1));
                 switch (Helper::BigEndian2Int(substr($data, 3, 1))) {
                     case 2:
                         $datedisplayformat = 'D/M/Y';
                         break;
                     case 1:
                         $datedisplayformat = 'M/D/Y';
                         break;
                     case 0:
                     default:
                         $datedisplayformat = 'Y/M/D';
                         break;
                 }
                 $data = array('timezone' => floatval($timezone), 'dst' => $dst, 'display' => $datedisplayformat);
                 break;
             case 0x2000083:
                 // LensType
                 $data = array('mf' => (bool) ($data & 0x1), 'd' => (bool) ($data & 0x2), 'g' => (bool) ($data & 0x4), 'vr' => (bool) ($data & 0x8));
                 break;
         }
         $tag_name = isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x' . str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT);
         $parsed[$tag_name] = $data;
     }
     return $parsed;
 }
コード例 #8
0
ファイル: Matroska.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type $EBMLstring
  *
  * @return type
  *
  * @link http://matroska.org/specs/
  */
 private static function EBML2Int($EBMLstring)
 {
     // Element ID coded with an UTF-8 like system:
     // 1xxx xxxx                                  - Class A IDs (2^7 -2 possible values) (base 0x8X)
     // 01xx xxxx  xxxx xxxx                       - Class B IDs (2^14-2 possible values) (base 0x4X 0xXX)
     // 001x xxxx  xxxx xxxx  xxxx xxxx            - Class C IDs (2^21-2 possible values) (base 0x2X 0xXX 0xXX)
     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - Class D IDs (2^28-2 possible values) (base 0x1X 0xXX 0xXX 0xXX)
     // Values with all x at 0 and 1 are reserved (hence the -2).
     // Data size, in octets, is also coded with an UTF-8 like system :
     // 1xxx xxxx                                                                              - value 0 to  2^7-2
     // 01xx xxxx  xxxx xxxx                                                                   - value 0 to 2^14-2
     // 001x xxxx  xxxx xxxx  xxxx xxxx                                                        - value 0 to 2^21-2
     // 0001 xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                             - value 0 to 2^28-2
     // 0000 1xxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                                  - value 0 to 2^35-2
     // 0000 01xx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx                       - value 0 to 2^42-2
     // 0000 001x  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx            - value 0 to 2^49-2
     // 0000 0001  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx  xxxx xxxx - value 0 to 2^56-2
     $first_byte_int = ord($EBMLstring[0]);
     if (0x80 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x7f);
     } elseif (0x40 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x3f);
     } elseif (0x20 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x1f);
     } elseif (0x10 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0xf);
     } elseif (0x8 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x7);
     } elseif (0x4 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x3);
     } elseif (0x2 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x1);
     } elseif (0x1 & $first_byte_int) {
         $EBMLstring[0] = chr($first_byte_int & 0x0);
     }
     return Helper::BigEndian2Int($EBMLstring);
 }
コード例 #9
0
ファイル: Flv.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
     $FLVheader = fread($this->getid3->fp, 5);
     $info['fileformat'] = 'flv';
     $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
     $info['flv']['header']['version'] = Helper::BigEndian2Int(substr($FLVheader, 3, 1));
     $TypeFlags = Helper::BigEndian2Int(substr($FLVheader, 4, 1));
     $magic = 'FLV';
     if ($info['flv']['header']['signature'] != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($info['flv']['header']['signature']) . '"';
         unset($info['flv']);
         unset($info['fileformat']);
         return false;
     }
     $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x4);
     $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x1);
     $FrameSizeDataLength = Helper::BigEndian2Int(fread($this->getid3->fp, 4));
     $FLVheaderFrameLength = 9;
     if ($FrameSizeDataLength > $FLVheaderFrameLength) {
         fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
     }
     $Duration = 0;
     $found_video = false;
     $found_audio = false;
     $found_meta = false;
     $found_valid_meta_playtime = false;
     $tagParseCount = 0;
     $info['flv']['framecount'] = array('total' => 0, 'audio' => 0, 'video' => 0);
     $flv_framecount =& $info['flv']['framecount'];
     while (ftell($this->getid3->fp) + 16 < $info['avdataend'] && ($tagParseCount++ <= $this->max_frames || !$found_valid_meta_playtime)) {
         $ThisTagHeader = fread($this->getid3->fp, 16);
         $PreviousTagLength = Helper::BigEndian2Int(substr($ThisTagHeader, 0, 4));
         $TagType = Helper::BigEndian2Int(substr($ThisTagHeader, 4, 1));
         $DataLength = Helper::BigEndian2Int(substr($ThisTagHeader, 5, 3));
         $Timestamp = Helper::BigEndian2Int(substr($ThisTagHeader, 8, 3));
         $LastHeaderByte = Helper::BigEndian2Int(substr($ThisTagHeader, 15, 1));
         $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength;
         if ($Timestamp > $Duration) {
             $Duration = $Timestamp;
         }
         ++$flv_framecount['total'];
         switch ($TagType) {
             case self::GETID3_FLV_TAG_AUDIO:
                 $flv_framecount['audio']++;
                 if (!$found_audio) {
                     $found_audio = true;
                     $info['flv']['audio']['audioFormat'] = $LastHeaderByte >> 4 & 0xf;
                     $info['flv']['audio']['audioRate'] = $LastHeaderByte >> 2 & 0x3;
                     $info['flv']['audio']['audioSampleSize'] = $LastHeaderByte >> 1 & 0x1;
                     $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x1;
                 }
                 break;
             case self::GETID3_FLV_TAG_VIDEO:
                 $flv_framecount['video']++;
                 if (!$found_video) {
                     $found_video = true;
                     $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x7;
                     $FLVvideoHeader = fread($this->getid3->fp, 11);
                     if ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H264) {
                         // this code block contributed by: moysevichØgmail*com
                         $AVCPacketType = Helper::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
                         if ($AVCPacketType == AVCSequenceParameterSetReader::H264_AVC_SEQUENCE_HEADER) {
                             //	read AVCDecoderConfigurationRecord
                             $configurationVersion = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 1));
                             $AVCProfileIndication = Helper::BigEndian2Int(substr($FLVvideoHeader, 5, 1));
                             $profile_compatibility = Helper::BigEndian2Int(substr($FLVvideoHeader, 6, 1));
                             $lengthSizeMinusOne = Helper::BigEndian2Int(substr($FLVvideoHeader, 7, 1));
                             $numOfSequenceParameterSets = Helper::BigEndian2Int(substr($FLVvideoHeader, 8, 1));
                             if (($numOfSequenceParameterSets & 0x1f) != 0) {
                                 //	there is at least one SequenceParameterSet
                                 //	read size of the first SequenceParameterSet
                                 //$spsSize = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
                                 $spsSize = Helper::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
                                 //	read the first SequenceParameterSet
                                 $sps = fread($this->getid3->fp, $spsSize);
                                 if (strlen($sps) == $spsSize) {
                                     //	make sure that whole SequenceParameterSet was red
                                     $spsReader = new AVCSequenceParameterSetReader($sps);
                                     $spsReader->readData();
                                     $info['video']['resolution_x'] = $spsReader->getWidth();
                                     $info['video']['resolution_y'] = $spsReader->getHeight();
                                 }
                             }
                         }
                         // end: moysevichØgmail*com
                     } elseif ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H263) {
                         $PictureSizeType = Helper::BigEndian2Int(substr($FLVvideoHeader, 3, 2)) >> 7;
                         $PictureSizeType = $PictureSizeType & 0x7;
                         $info['flv']['header']['videoSizeType'] = $PictureSizeType;
                         switch ($PictureSizeType) {
                             case 0:
                                 //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
                                 //$PictureSizeEnc <<= 1;
                                 //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
                                 //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
                                 //$PictureSizeEnc <<= 1;
                                 //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
                                 $PictureSizeEnc['x'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 2));
                                 $PictureSizeEnc['y'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
                                 $PictureSizeEnc['x'] >>= 7;
                                 $PictureSizeEnc['y'] >>= 7;
                                 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xff;
                                 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xff;
                                 break;
                             case 1:
                                 $PictureSizeEnc['x'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
                                 $PictureSizeEnc['y'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
                                 $PictureSizeEnc['x'] >>= 7;
                                 $PictureSizeEnc['y'] >>= 7;
                                 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xffff;
                                 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xffff;
                                 break;
                             case 2:
                                 $info['video']['resolution_x'] = 352;
                                 $info['video']['resolution_y'] = 288;
                                 break;
                             case 3:
                                 $info['video']['resolution_x'] = 176;
                                 $info['video']['resolution_y'] = 144;
                                 break;
                             case 4:
                                 $info['video']['resolution_x'] = 128;
                                 $info['video']['resolution_y'] = 96;
                                 break;
                             case 5:
                                 $info['video']['resolution_x'] = 320;
                                 $info['video']['resolution_y'] = 240;
                                 break;
                             case 6:
                                 $info['video']['resolution_x'] = 160;
                                 $info['video']['resolution_y'] = 120;
                                 break;
                             default:
                                 $info['video']['resolution_x'] = 0;
                                 $info['video']['resolution_y'] = 0;
                                 break;
                         }
                     }
                     $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
                 }
                 break;
                 // Meta tag
             // Meta tag
             case self::GETID3_FLV_TAG_META:
                 if (!$found_meta) {
                     $found_meta = true;
                     fseek($this->getid3->fp, -1, SEEK_CUR);
                     $datachunk = fread($this->getid3->fp, $DataLength);
                     $AMFstream = new AMFStream($datachunk);
                     $reader = new AMFReader($AMFstream);
                     $eventName = $reader->readData();
                     $info['flv']['meta'][$eventName] = $reader->readData();
                     unset($reader);
                     $copykeys = array('framerate' => 'frame_rate', 'width' => 'resolution_x', 'height' => 'resolution_y', 'audiodatarate' => 'bitrate', 'videodatarate' => 'bitrate');
                     foreach ($copykeys as $sourcekey => $destkey) {
                         if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) {
                             switch ($sourcekey) {
                                 case 'width':
                                 case 'height':
                                     $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
                                     break;
                                 case 'audiodatarate':
                                     $info['audio'][$destkey] = Helper::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
                                     break;
                                 case 'videodatarate':
                                 case 'frame_rate':
                                 default:
                                     $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
                                     break;
                             }
                         }
                     }
                     if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
                         $found_valid_meta_playtime = true;
                     }
                 }
                 break;
             default:
                 // noop
                 break;
         }
         fseek($this->getid3->fp, $NextOffset, SEEK_SET);
     }
     $info['playtime_seconds'] = $Duration / 1000;
     if ($info['playtime_seconds'] > 0) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if ($info['flv']['header']['hasAudio']) {
         $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']);
         $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']);
         $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']);
         $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1;
         // 0=mono,1=stereo
         $info['audio']['lossless'] = $info['flv']['audio']['audioFormat'] ? false : true;
         // 0=uncompressed
         $info['audio']['dataformat'] = 'flv';
     }
     if (!empty($info['flv']['header']['hasVideo'])) {
         $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']);
         $info['video']['dataformat'] = 'flv';
         $info['video']['lossless'] = false;
     }
     // Set information from meta
     if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
         $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration'];
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
         $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']);
     }
     if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
         $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']);
     }
     return true;
 }
コード例 #10
0
ファイル: GetId3Core.php プロジェクト: Nattpyre/rocketfiles
 /**
  * public: analyze file
  *
  * @param  type      $filename
  *
  * @return type
  *
  * @throws Exception
  */
 public function analyze($filename)
 {
     try {
         if (!$this->openfile($filename)) {
             return $this->info;
         }
         // Handle tags
         foreach (array('id3v2' => 'id3v2', 'id3v1' => 'id3v1', 'apetag' => 'ape', 'lyrics3' => 'lyrics3') as $tag_name => $tag_key) {
             $option_tag = 'option_tag_' . $tag_name;
             if ($this->{$option_tag}) {
                 try {
                     $tag_class = 'Helpers\\GetId3\\Module\\Tag\\' . ucfirst($tag_name);
                     $tag = new $tag_class($this);
                     $tag->analyze();
                 } catch (DefaultException $e) {
                     throw $e;
                 }
             }
         }
         if (isset($this->info['id3v2']['tag_offset_start'])) {
             $this->info['avdataoffset'] = max($this->info['avdataoffset'], $this->info['id3v2']['tag_offset_end']);
         }
         foreach (array('id3v1' => 'id3v1', 'apetag' => 'ape', 'lyrics3' => 'lyrics3') as $tag_name => $tag_key) {
             if (isset($this->info[$tag_key]['tag_offset_start'])) {
                 $this->info['avdataend'] = min($this->info['avdataend'], $this->info[$tag_key]['tag_offset_start']);
             }
         }
         // ID3v2 detection (NOT parsing), even if ($this->option_tag_id3v2 == false) done to make fileformat easier
         if (!$this->getOptionTagId3v2()) {
             fseek($this->getFp(), 0, SEEK_SET);
             $header = fread($this->getFp(), 10);
             if (substr($header, 0, 3) == 'ID3' && strlen($header) == 10) {
                 $this->info['id3v2']['header'] = true;
                 $this->info['id3v2']['majorversion'] = ord($header[3]);
                 $this->info['id3v2']['minorversion'] = ord($header[4]);
                 $this->info['avdataoffset'] += Helper::BigEndian2Int(substr($header, 6, 4), 1) + 10;
                 // length of ID3v2 tag in 10-byte header doesn't include 10-byte header length
             }
         }
         // read 32 kb file data
         fseek($this->getFp(), $this->info['avdataoffset'], SEEK_SET);
         $formattest = fread($this->getFp(), 32774);
         // determine format
         $determined_format = $this->GetFileFormat($formattest, $filename);
         // unable to determine file format
         if (!$determined_format) {
             fclose($this->getFp());
             return $this->error('unable to determine file format');
         }
         // check for illegal ID3 tags
         if (isset($determined_format['fail_id3']) && (in_array('id3v1', $this->info['tags']) || in_array('id3v2', $this->info['tags']))) {
             if ($determined_format['fail_id3'] === 'ERROR') {
                 fclose($this->getFp());
                 return $this->error('ID3 tags not allowed on this file type.');
             } elseif ($determined_format['fail_id3'] === 'WARNING') {
                 $this->warning('ID3 tags not allowed on this file type.');
             }
         }
         // check for illegal APE tags
         if (isset($determined_format['fail_ape']) && in_array('ape', $this->info['tags'])) {
             if ($determined_format['fail_ape'] === 'ERROR') {
                 fclose($this->getFp());
                 return $this->error('APE tags not allowed on this file type.');
             } elseif ($determined_format['fail_ape'] === 'WARNING') {
                 $this->warning('APE tags not allowed on this file type.');
             }
         }
         // set mime type
         $this->info['mime_type'] = $determined_format['mime_type'];
         // supported format signature pattern detected, but module deleted
         if (!class_exists($determined_format['class'])) {
             fclose($this->getFp());
             return $this->error('Format not supported, module "' . $determined_format['include'] . '" was removed.');
         }
         // module requires iconv support
         // Check encoding/iconv support
         if (!empty($determined_format['iconv_req']) && !function_exists('iconv') && !in_array($this->getEncoding(), array('ISO-8859-1', 'UTF-8', 'UTF-16LE', 'UTF-16BE', 'UTF-16'))) {
             $errormessage = 'iconv() support is required for this module (' . $determined_format['include'] . ') for encodings other than ISO-8859-1, UTF-8, UTF-16LE, UTF16-BE, UTF-16. ';
             if (self::$EnvironmentIsWindows) {
                 $errormessage .= 'PHP does not have iconv() support. Please enable php_iconv.dll in php.ini, and copy iconv.dll from c:/php/dlls to c:/windows/system32';
             } else {
                 $errormessage .= 'PHP is not compiled with iconv() support. Please recompile with the --with-iconv switch';
             }
             return $this->error($errormessage);
         }
         // instantiate module class
         $class_name = 'Helpers\\GetId3\\Module\\' . Helper::toCamelCase($determined_format['group'], '-', true) . '\\' . ucfirst($determined_format['module']);
         if (!class_exists($class_name)) {
             return $this->error('Format not supported, module "' . $determined_format['include'] . '" is corrupt.');
         }
         //if (isset($determined_format['option'])) {
         //	//$class = new $class_name($this->fp, $this->info, $determined_format['option']);
         //} else {
         //$class = new $class_name($this->fp, $this->info);
         $class = new $class_name($this);
         //}
         if (!empty($determined_format['set_inline_attachments'])) {
             $class->inline_attachments = $this->option_save_attachments;
         }
         $class->analyze();
         unset($class);
         // close file
         fclose($this->getFp());
         // process all tags - copy to 'tags' and convert charsets
         if ($this->getOptionTagsProcess()) {
             $this->HandleAllTags();
         }
         // perform more calculations
         if ($this->getOptionExtraInfo()) {
             $this->ChannelsBitratePlaytimeCalculations();
             $this->CalculateCompressionRatioVideo();
             $this->CalculateCompressionRatioAudio();
             $this->CalculateReplayGain();
             $this->ProcessAudioStreams();
         }
         // get the MD5 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
         if ($this->getOptionMD5Data()) {
             // do not cald md5_data if md5_data_source is present - set by flac only - future MPC/SV8 too
             if (!$this->getOptionMD5DataDource() || empty($this->info['md5_data_source'])) {
                 $this->getHashdata('md5');
             }
         }
         // get the SHA1 sum of the audio/video portion of the file - without ID3/APE/Lyrics3/etc header/footer tags
         if ($this->getOptionSha1Data()) {
             $this->getHashdata('sha1');
         }
         // remove undesired keys
         $this->CleanUp();
     } catch (\Exception $e) {
         $this->error('Caught exception: ' . $e->getMessage());
     }
     // return info array
     return $this->info;
 }
コード例 #11
0
ファイル: Id3v2.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type    $parsedFrame
  *
  * @return bool
  */
 public function ParseID3v2Frame(&$parsedFrame)
 {
     // shortcuts
     $info =& $this->getid3->info;
     $id3v2_majorversion = $info['id3v2']['majorversion'];
     $parsedFrame['framenamelong'] = $this->FrameNameLongLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenamelong'])) {
         unset($parsedFrame['framenamelong']);
     }
     $parsedFrame['framenameshort'] = $this->FrameNameShortLookup($parsedFrame['frame_name']);
     if (empty($parsedFrame['framenameshort'])) {
         unset($parsedFrame['framenameshort']);
     }
     if ($id3v2_majorversion >= 3) {
         // frame flags are not part of the ID3v2.2 standard
         if ($id3v2_majorversion == 3) {
             //    Frame Header Flags
             //    %abc00000 %ijk00000
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // c - Read only
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x80);
             // i - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // j - Encryption
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x20);
             // k - Grouping identity
         } elseif ($id3v2_majorversion == 4) {
             //    Frame Header Flags
             //    %0abc0000 %0h00kmnp
             $parsedFrame['flags']['TagAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4000);
             // a - Tag alter preservation
             $parsedFrame['flags']['FileAlterPreservation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2000);
             // b - File alter preservation
             $parsedFrame['flags']['ReadOnly'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1000);
             // c - Read only
             $parsedFrame['flags']['GroupingIdentity'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x40);
             // h - Grouping identity
             $parsedFrame['flags']['compression'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x8);
             // k - Compression
             $parsedFrame['flags']['Encryption'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x4);
             // m - Encryption
             $parsedFrame['flags']['Unsynchronisation'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x2);
             // n - Unsynchronisation
             $parsedFrame['flags']['DataLengthIndicator'] = (bool) ($parsedFrame['frame_flags_raw'] & 0x1);
             // p - Data length indicator
             // Frame-level de-unsynchronisation - ID3v2.4
             if ($parsedFrame['flags']['Unsynchronisation']) {
                 $parsedFrame['data'] = $this->DeUnsynchronise($parsedFrame['data']);
             }
             if ($parsedFrame['flags']['DataLengthIndicator']) {
                 $parsedFrame['data_length_indicator'] = Helper::BigEndian2Int(substr($parsedFrame['data'], 0, 4), 1);
                 $parsedFrame['data'] = substr($parsedFrame['data'], 4);
             }
         }
         //    Frame-level de-compression
         if ($parsedFrame['flags']['compression']) {
             $parsedFrame['decompressed_size'] = Helper::BigEndian2Int(substr($parsedFrame['data'], 0, 4));
             if (!function_exists('gzuncompress')) {
                 $info['warning'][] = 'gzuncompress() support required to decompress ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
             } else {
                 if ($decompresseddata = @gzuncompress(substr($parsedFrame['data'], 4))) {
                     //if ($decompresseddata = @gzuncompress($parsedFrame['data'])) {
                     $parsedFrame['data'] = $decompresseddata;
                     unset($decompresseddata);
                 } else {
                     $info['warning'][] = 'gzuncompress() failed on compressed contents of ID3v2 frame "' . $parsedFrame['frame_name'] . '"';
                 }
             }
         }
     }
     if (!empty($parsedFrame['flags']['DataLengthIndicator'])) {
         if ($parsedFrame['data_length_indicator'] != strlen($parsedFrame['data'])) {
             $info['warning'][] = 'ID3v2 frame "' . $parsedFrame['frame_name'] . '" should be ' . $parsedFrame['data_length_indicator'] . ' bytes long according to DataLengthIndicator, but found ' . strlen($parsedFrame['data']) . ' bytes of data';
         }
     }
     if (isset($parsedFrame['datalength']) && $parsedFrame['datalength'] == 0) {
         $warning = 'Frame "' . $parsedFrame['frame_name'] . '" at offset ' . $parsedFrame['dataoffset'] . ' has no data portion';
         switch ($parsedFrame['frame_name']) {
             case 'WCOM':
                 $warning .= ' (this is known to happen with files tagged by RioPort)';
                 break;
             default:
                 break;
         }
         $info['warning'][] = $warning;
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'UFID' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'UFI') {
         // 4.1   UFI  Unique file identifier
         //   There may be more than one 'UFID' frame in a tag,
         //   but only one with the same 'Owner identifier'.
         // <Header for 'Unique file identifier', ID: 'UFID'>
         // Owner identifier        <text string> $00
         // Identifier              <up to 64 bytes binary data>
         $exploded = explode("", $parsedFrame['data'], 2);
         $parsedFrame['ownerid'] = isset($exploded[0]) ? $exploded[0] : '';
         $parsedFrame['data'] = isset($exploded[1]) ? $exploded[1] : '';
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'TXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'TXX') {
         // 4.2.2 TXX  User defined text information frame
         //   There may be more than one 'TXXX' frame in each tag,
         //   but only one with the same description.
         // <Header for 'User defined text information frame', ID: 'TXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // Value             <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = trim(Helper::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']));
         }
         //unset($parsedFrame['data']); do not unset, may be needed elsewhere, e.g. for replaygain
     } elseif ($parsedFrame['frame_name'][0] == 'T') {
         // 4.2. T??[?] Text information frame
         //   There may only be one text information frame of its kind in an tag.
         // <Header for 'Text information frame', ID: 'T000' - 'TZZZ',
         // excluding 'TXXX' described in 4.2.6.>
         // Text encoding                $xx
         // Information                  <text string(s) according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $string = Helper::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
             $string = rtrim($string, "");
             // remove possible terminating null (put by encoding id or software bug)
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $string;
             unset($string);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'WXXX' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'WXX') {
         // 4.3.2 WXX  User defined URL link frame
         //   There may be more than one 'WXXX' frame in each tag,
         //   but only one with the same description
         // <Header for 'User defined URL link frame', ID: 'WXXX'>
         // Text encoding     $xx
         // Description       <text string according to encoding> $00 (00)
         // URL               <text string>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding));
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         if ($frame_terminatorpos) {
             // there are null bytes after the data - this is not according to spec
             // only use data up to first null byte
             $frame_urldata = (string) substr($parsedFrame['data'], 0, $frame_terminatorpos);
         } else {
             // no null bytes following data, just use all data
             $frame_urldata = (string) $parsedFrame['data'];
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['url'] = $frame_urldata;
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = Helper::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($parsedFrame['frame_name'][0] == 'W') {
         // 4.3. W??? URL link frames
         //   There may only be one URL link frame of its kind in a tag,
         //   except when stated otherwise in the frame description
         // <Header for 'URL link frame', ID: 'W000' - 'WZZZ', excluding 'WXXX'
         // described in 4.3.2.>
         // URL              <text string>
         $parsedFrame['url'] = trim($parsedFrame['data']);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['url'];
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'IPLS' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'IPL') {
         // 4.4  IPL  Involved people list (ID3v2.2 only)
         //   There may only be one 'IPL' frame in each tag
         // <Header for 'User defined URL link frame', ID: 'IPL'>
         // Text encoding     $xx
         // People list strings    <textstrings>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($parsedFrame['encodingid']);
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = Helper::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MCDI' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MCI') {
         // 4.5   MCI  Music CD identifier
         //   There may only be one 'MCDI' frame in each tag
         // <Header for 'Music CD identifier', ID: 'MCDI'>
         // CD TOC                <binary data>
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = $parsedFrame['data'];
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ETCO' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ETC') {
         // 4.6   ETC  Event timing codes
         //   There may only be one 'ETCO' frame in each tag
         // <Header for 'Event timing codes', ID: 'ETCO'>
         // Time stamp format    $xx
         //   Where time stamp format is:
         // $01  (32-bit value) MPEG frames from beginning of file
         // $02  (32-bit value) milliseconds from beginning of file
         //   Followed by a list of key events in the following format:
         // Type of event   $xx
         // Time stamp      $xx (xx ...)
         //   The 'Time stamp' is set to zero if directly at the beginning of the sound
         //   or after the previous event. All events MUST be sorted in chronological order.
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame['typeid'] = substr($parsedFrame['data'], $frame_offset++, 1);
             $parsedFrame['type'] = $this->ETCOEventLookup($parsedFrame['typeid']);
             $parsedFrame['timestamp'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'MLLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'MLL') {
         // 4.7   MLL MPEG location lookup table
         //   There may only be one 'MLLT' frame in each tag
         // <Header for 'Location lookup table', ID: 'MLLT'>
         // MPEG frames between reference  $xx xx
         // Bytes between reference        $xx xx xx
         // Milliseconds between reference $xx xx xx
         // Bits for bytes deviation       $xx
         // Bits for milliseconds dev.     $xx
         //   Then for every reference the following data is included;
         // Deviation in bytes         %xxx....
         // Deviation in milliseconds  %xxx....
         $frame_offset = 0;
         $parsedFrame['framesbetweenreferences'] = Helper::BigEndian2Int(substr($parsedFrame['data'], 0, 2));
         $parsedFrame['bytesbetweenreferences'] = Helper::BigEndian2Int(substr($parsedFrame['data'], 2, 3));
         $parsedFrame['msbetweenreferences'] = Helper::BigEndian2Int(substr($parsedFrame['data'], 5, 3));
         $parsedFrame['bitsforbytesdeviation'] = Helper::BigEndian2Int(substr($parsedFrame['data'], 8, 1));
         $parsedFrame['bitsformsdeviation'] = Helper::BigEndian2Int(substr($parsedFrame['data'], 9, 1));
         $parsedFrame['data'] = substr($parsedFrame['data'], 10);
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $deviationbitstream .= Helper::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         }
         $reference_counter = 0;
         while (strlen($deviationbitstream) > 0) {
             $parsedFrame[$reference_counter]['bytedeviation'] = bindec(substr($deviationbitstream, 0, $parsedFrame['bitsforbytesdeviation']));
             $parsedFrame[$reference_counter]['msdeviation'] = bindec(substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'], $parsedFrame['bitsformsdeviation']));
             $deviationbitstream = substr($deviationbitstream, $parsedFrame['bitsforbytesdeviation'] + $parsedFrame['bitsformsdeviation']);
             ++$reference_counter;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYTC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'STC') {
         // 4.8   STC  Synchronised tempo codes
         //   There may only be one 'SYTC' frame in each tag
         // <Header for 'Synchronised tempo codes', ID: 'SYTC'>
         // Time stamp format   $xx
         // Tempo data          <binary data>
         //   Where time stamp format is:
         // $01  (32-bit value) MPEG frames from beginning of file
         // $02  (32-bit value) milliseconds from beginning of file
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $timestamp_counter = 0;
         while ($frame_offset < strlen($parsedFrame['data'])) {
             $parsedFrame[$timestamp_counter]['tempo'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             if ($parsedFrame[$timestamp_counter]['tempo'] == 255) {
                 $parsedFrame[$timestamp_counter]['tempo'] += ord(substr($parsedFrame['data'], $frame_offset++, 1));
             }
             $parsedFrame[$timestamp_counter]['timestamp'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
             $frame_offset += 4;
             ++$timestamp_counter;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'ULT') {
         // 4.9   ULT  Unsynchronised lyric/text transcription
         //   There may be more than one 'Unsynchronised lyrics/text transcription' frame
         //   in each tag, but only one with the same language and content descriptor.
         // <Header for 'Unsynchronised lyrics/text transcription', ID: 'USLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Content descriptor   <text string according to encoding> $00 (00)
         // Lyrics/text          <full text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = $parsedFrame['data'];
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['description'] = $frame_description;
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = Helper::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'SYLT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'SLT') {
         // 4.10  SLT  Synchronised lyric/text
         //   There may be more than one 'SYLT' frame in each tag,
         //   but only one with the same language and content descriptor.
         // <Header for 'Synchronised lyrics/text', ID: 'SYLT'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // Time stamp format    $xx
         //   $01  (32-bit value) MPEG frames from beginning of file
         //   $02  (32-bit value) milliseconds from beginning of file
         // Content type         $xx
         // Content descriptor   <text string according to encoding> $00 (00)
         //   Terminated text to be synced (typically a syllable)
         //   Sync identifier (terminator to above string)   $00 (00)
         //   Time stamp                                     $xx (xx ...)
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttypeid'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['contenttype'] = $this->SYTLContentTypeLookup($parsedFrame['contenttypeid']);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $timestampindex = 0;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata)) {
             $frame_offset = 0;
             $frame_terminatorpos = strpos($frame_remainingdata, $this->TextEncodingTerminatorLookup($frame_textencoding));
             if ($frame_terminatorpos === false) {
                 $frame_remainingdata = '';
             } else {
                 if (ord(substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
                     ++$frame_terminatorpos;
                     // strpos() fooled because 2nd byte of Unicode chars are often 0x00
                 }
                 $parsedFrame['lyrics'][$timestampindex]['data'] = substr($frame_remainingdata, $frame_offset, $frame_terminatorpos - $frame_offset);
                 $frame_remainingdata = substr($frame_remainingdata, $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
                 if ($timestampindex == 0 && ord($frame_remainingdata[0]) != 0) {
                     // timestamp probably omitted for first data item
                 } else {
                     $parsedFrame['lyrics'][$timestampindex]['timestamp'] = Helper::BigEndian2Int(substr($frame_remainingdata, 0, 4));
                     $frame_remainingdata = substr($frame_remainingdata, 4);
                 }
                 ++$timestampindex;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'COM') {
         // 4.11  COM  Comments
         //   There may be more than one comment frame in each tag,
         //   but only one with the same language and content descriptor.
         // <Header for 'Comment', ID: 'COMM'>
         // Text encoding          $xx
         // Language               $xx xx xx
         // Short content descrip. <text string according to encoding> $00 (00)
         // The actual text        <full text string according to encoding>
         if (strlen($parsedFrame['data']) < 5) {
             $info['warning'][] = 'Invalid data (too short) for "' . $parsedFrame['frame_name'] . '" frame at offset ' . $parsedFrame['dataoffset'];
         } else {
             $frame_offset = 0;
             $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
             if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
                 $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
             }
             $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
             $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
             if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
                 ++$frame_terminatorpos;
                 // strpos() fooled because 2nd byte of Unicode chars are often 0x00
             }
             $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_description) === 0) {
                 $frame_description = '';
             }
             $frame_text = (string) substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
             $parsedFrame['encodingid'] = $frame_textencoding;
             $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
             $parsedFrame['language'] = $frame_language;
             $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
             $parsedFrame['description'] = $frame_description;
             $parsedFrame['data'] = $frame_text;
             if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
                 $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = Helper::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
             }
         }
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'RVA2') {
         // 4.11  RVA2 Relative volume adjustment (2) (ID3v2.4+ only)
         //   There may be more than one 'RVA2' frame in each tag,
         //   but only one with the same identification string
         // <Header for 'Relative volume adjustment (2)', ID: 'RVA2'>
         // Identification          <text string> $00
         //   The 'identification' string is used to identify the situation and/or
         //   device where this adjustment should apply. The following is then
         //   repeated for every channel:
         // Type of channel         $xx
         // Volume adjustment       $xx xx
         // Bits representing peak  $xx
         // Peak volume             $xx (xx ...)
         $frame_terminatorpos = strpos($parsedFrame['data'], "");
         $frame_idstring = substr($parsedFrame['data'], 0, $frame_terminatorpos);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         $parsedFrame['description'] = $frame_idstring;
         $RVA2channelcounter = 0;
         while (strlen($frame_remainingdata) >= 5) {
             $frame_offset = 0;
             $frame_channeltypeid = ord(substr($frame_remainingdata, $frame_offset++, 1));
             $parsedFrame[$RVA2channelcounter]['channeltypeid'] = $frame_channeltypeid;
             $parsedFrame[$RVA2channelcounter]['channeltype'] = $this->RVA2ChannelTypeLookup($frame_channeltypeid);
             $parsedFrame[$RVA2channelcounter]['volumeadjust'] = Helper::BigEndian2Int(substr($frame_remainingdata, $frame_offset, 2), false, true);
             // 16-bit signed
             $frame_offset += 2;
             $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] = ord(substr($frame_remainingdata, $frame_offset++, 1));
             if ($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] < 1 || $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] > 4) {
                 $info['warning'][] = 'ID3v2::RVA2 frame[' . $RVA2channelcounter . '] contains invalid ' . $parsedFrame[$RVA2channelcounter]['bitspeakvolume'] . '-byte bits-representing-peak value';
                 break;
             }
             $frame_bytespeakvolume = ceil($parsedFrame[$RVA2channelcounter]['bitspeakvolume'] / 8);
             $parsedFrame[$RVA2channelcounter]['peakvolume'] = Helper::BigEndian2Int(substr($frame_remainingdata, $frame_offset, $frame_bytespeakvolume));
             $frame_remainingdata = substr($frame_remainingdata, $frame_offset + $frame_bytespeakvolume);
             ++$RVA2channelcounter;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'RVAD' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'RVA') {
         // 4.12  RVA  Relative volume adjustment (ID3v2.2 only)
         //   There may only be one 'RVA' frame in each tag
         // <Header for 'Relative volume adjustment', ID: 'RVA'>
         // ID3v2.2 => Increment/decrement     %000000ba
         // ID3v2.3 => Increment/decrement     %00fedcba
         // Bits used for volume descr.        $xx
         // Relative volume change, right      $xx xx (xx ...) // a
         // Relative volume change, left       $xx xx (xx ...) // b
         // Peak volume right                  $xx xx (xx ...)
         // Peak volume left                   $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, right back $xx xx (xx ...) // c
         // Relative volume change, left back  $xx xx (xx ...) // d
         // Peak volume right back             $xx xx (xx ...)
         // Peak volume left back              $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, center     $xx xx (xx ...) // e
         // Peak volume center                 $xx xx (xx ...)
         //   ID3v2.3 only, optional (not present in ID3v2.2):
         // Relative volume change, bass       $xx xx (xx ...) // f
         // Peak volume bass                   $xx xx (xx ...)
         $frame_offset = 0;
         $frame_incrdecrflags = Helper::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['incdec']['right'] = (bool) substr($frame_incrdecrflags, 6, 1);
         $parsedFrame['incdec']['left'] = (bool) substr($frame_incrdecrflags, 7, 1);
         $parsedFrame['bitsvolume'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesvolume = ceil($parsedFrame['bitsvolume'] / 8);
         $parsedFrame['volumechange']['right'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['right'] === false) {
             $parsedFrame['volumechange']['right'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['volumechange']['left'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         if ($parsedFrame['incdec']['left'] === false) {
             $parsedFrame['volumechange']['left'] *= -1;
         }
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['right'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         $parsedFrame['peakvolume']['left'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
         $frame_offset += $frame_bytesvolume;
         if ($id3v2_majorversion == 3) {
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['rightrear'] = (bool) substr($frame_incrdecrflags, 4, 1);
                 $parsedFrame['incdec']['leftrear'] = (bool) substr($frame_incrdecrflags, 5, 1);
                 $parsedFrame['volumechange']['rightrear'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['rightrear'] === false) {
                     $parsedFrame['volumechange']['rightrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['volumechange']['leftrear'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['leftrear'] === false) {
                     $parsedFrame['volumechange']['leftrear'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['rightrear'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['leftrear'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['center'] = (bool) substr($frame_incrdecrflags, 3, 1);
                 $parsedFrame['volumechange']['center'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['center'] === false) {
                     $parsedFrame['volumechange']['center'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['center'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_offset);
             if (strlen($parsedFrame['data']) > 0) {
                 $parsedFrame['incdec']['bass'] = (bool) substr($frame_incrdecrflags, 2, 1);
                 $parsedFrame['volumechange']['bass'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 if ($parsedFrame['incdec']['bass'] === false) {
                     $parsedFrame['volumechange']['bass'] *= -1;
                 }
                 $frame_offset += $frame_bytesvolume;
                 $parsedFrame['peakvolume']['bass'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesvolume));
                 $frame_offset += $frame_bytesvolume;
             }
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'EQU2') {
         // 4.12  EQU2 Equalisation (2) (ID3v2.4+ only)
         //   There may be more than one 'EQU2' frame in each tag,
         //   but only one with the same identification string
         // <Header of 'Equalisation (2)', ID: 'EQU2'>
         // Interpolation method  $xx
         //   $00  Band
         //   $01  Linear
         // Identification        <text string> $00
         //   The following is then repeated for every adjustment point
         // Frequency          $xx xx
         // Volume adjustment  $xx xx
         $frame_offset = 0;
         $frame_interpolationmethod = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_idstring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_idstring) === 0) {
             $frame_idstring = '';
         }
         $parsedFrame['description'] = $frame_idstring;
         $frame_remainingdata = substr($parsedFrame['data'], $frame_terminatorpos + strlen(""));
         while (strlen($frame_remainingdata)) {
             $frame_frequency = Helper::BigEndian2Int(substr($frame_remainingdata, 0, 2)) / 2;
             $parsedFrame['data'][$frame_frequency] = Helper::BigEndian2Int(substr($frame_remainingdata, 2, 2), false, true);
             $frame_remainingdata = substr($frame_remainingdata, 4);
         }
         $parsedFrame['interpolationmethod'] = $frame_interpolationmethod;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 3 && $parsedFrame['frame_name'] == 'EQUA' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'EQU') {
         // 4.13  EQU  Equalisation (ID3v2.2 only)
         //   There may only be one 'EQUA' frame in each tag
         // <Header for 'Relative volume adjustment', ID: 'EQU'>
         // Adjustment bits    $xx
         //   This is followed by 2 bytes + ('adjustment bits' rounded up to the
         //   nearest byte) for every equalisation band in the following format,
         //   giving a frequency range of 0 - 32767Hz:
         // Increment/decrement   %x (MSB of the Frequency)
         // Frequency             (lower 15 bits)
         // Adjustment            $xx (xx ...)
         $frame_offset = 0;
         $parsedFrame['adjustmentbits'] = substr($parsedFrame['data'], $frame_offset++, 1);
         $frame_adjustmentbytes = ceil($parsedFrame['adjustmentbits'] / 8);
         $frame_remainingdata = (string) substr($parsedFrame['data'], $frame_offset);
         while (strlen($frame_remainingdata) > 0) {
             $frame_frequencystr = Helper::BigEndian2Bin(substr($frame_remainingdata, 0, 2));
             $frame_incdec = (bool) substr($frame_frequencystr, 0, 1);
             $frame_frequency = bindec(substr($frame_frequencystr, 1, 15));
             $parsedFrame[$frame_frequency]['incdec'] = $frame_incdec;
             $parsedFrame[$frame_frequency]['adjustment'] = Helper::BigEndian2Int(substr($frame_remainingdata, 2, $frame_adjustmentbytes));
             if ($parsedFrame[$frame_frequency]['incdec'] === false) {
                 $parsedFrame[$frame_frequency]['adjustment'] *= -1;
             }
             $frame_remainingdata = substr($frame_remainingdata, 2 + $frame_adjustmentbytes);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RVRB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'REV') {
         // 4.14  REV  Reverb
         //   There may only be one 'RVRB' frame in each tag.
         // <Header for 'Reverb', ID: 'RVRB'>
         // Reverb left (ms)                 $xx xx
         // Reverb right (ms)                $xx xx
         // Reverb bounces, left             $xx
         // Reverb bounces, right            $xx
         // Reverb feedback, left to left    $xx
         // Reverb feedback, left to right   $xx
         // Reverb feedback, right to right  $xx
         // Reverb feedback, right to left   $xx
         // Premix left to right             $xx
         // Premix right to left             $xx
         $frame_offset = 0;
         $parsedFrame['left'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['right'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bouncesL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['bouncesR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['feedbackRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixLR'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['premixRL'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'APIC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'PIC') {
         // 4.15  PIC  Attached picture
         //   There may be several pictures attached to one file,
         //   each in their individual 'APIC' frame, but only one
         //   with the same content descriptor
         // <Header for 'Attached picture', ID: 'APIC'>
         // Text encoding      $xx
         // ID3v2.3+ => MIME type          <text string> $00
         // ID3v2.2  => Image format       $xx xx xx
         // Picture type       $xx
         // Description        <text string according to encoding> $00 (00)
         // Picture data       <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         if ($id3v2_majorversion == 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_imagetype = substr($parsedFrame['data'], $frame_offset, 3);
             if (strtolower($frame_imagetype) == 'ima') {
                 // complete hack for mp3Rage (www.chaoticsoftware.com) that puts ID3v2.3-formatted
                 // MIME type instead of 3-char ID3v2.2-format image type  (thanks xbhoffØpacbell*net)
                 $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
                 $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
                 if (ord($frame_mimetype) === 0) {
                     $frame_mimetype = '';
                 }
                 $frame_imagetype = strtoupper(str_replace('image/', '', strtolower($frame_mimetype)));
                 if ($frame_imagetype == 'JPEG') {
                     $frame_imagetype = 'JPG';
                 }
                 $frame_offset = $frame_terminatorpos + strlen("");
             } else {
                 $frame_offset += 3;
             }
         }
         if ($id3v2_majorversion > 2 && strlen($parsedFrame['data']) > $frame_offset) {
             $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
             $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_mimetype) === 0) {
                 $frame_mimetype = '';
             }
             $frame_offset = $frame_terminatorpos + strlen("");
         }
         $frame_picturetype = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($frame_offset >= $parsedFrame['datalength']) {
             $info['warning'][] = 'data portion of APIC frame is missing at offset ' . ($parsedFrame['dataoffset'] + 8 + $frame_offset);
         } else {
             $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
             if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
                 ++$frame_terminatorpos;
                 // strpos() fooled because 2nd byte of Unicode chars are often 0x00
             }
             $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
             if (ord($frame_description) === 0) {
                 $frame_description = '';
             }
             $parsedFrame['encodingid'] = $frame_textencoding;
             $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
             if ($id3v2_majorversion == 2) {
                 $parsedFrame['imagetype'] = $frame_imagetype;
             } else {
                 $parsedFrame['mime'] = $frame_mimetype;
             }
             $parsedFrame['picturetypeid'] = $frame_picturetype;
             $parsedFrame['picturetype'] = $this->APICPictureTypeLookup($frame_picturetype);
             $parsedFrame['description'] = $frame_description;
             $parsedFrame['data'] = substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)));
             $parsedFrame['datalength'] = strlen($parsedFrame['data']);
             $parsedFrame['image_mime'] = '';
             $imageinfo = array();
             $imagechunkcheck = Helper::GetDataImageSize($parsedFrame['data'], $imageinfo);
             if ($imagechunkcheck[2] >= 1 && $imagechunkcheck[2] <= 3) {
                 $parsedFrame['image_mime'] = 'image/' . Helper::ImageTypesLookup($imagechunkcheck[2]);
                 if ($imagechunkcheck[0]) {
                     $parsedFrame['image_width'] = $imagechunkcheck[0];
                 }
                 if ($imagechunkcheck[1]) {
                     $parsedFrame['image_height'] = $imagechunkcheck[1];
                 }
             }
             do {
                 if ($this->inline_attachments === false) {
                     // skip entirely
                     unset($parsedFrame['data']);
                     break;
                 }
                 if ($this->inline_attachments === true) {
                     // great
                 } elseif (is_int($this->inline_attachments)) {
                     if ($this->inline_attachments < $parsedFrame['data_length']) {
                         // too big, skip
                         $info['warning'][] = 'attachment at ' . $frame_offset . ' is too large to process inline (' . number_format($parsedFrame['data_length']) . ' bytes)';
                         unset($parsedFrame['data']);
                         break;
                     }
                 } elseif (is_string($this->inline_attachments)) {
                     $this->inline_attachments = rtrim(str_replace(array('/', '\\'), DIRECTORY_SEPARATOR, $this->inline_attachments), DIRECTORY_SEPARATOR);
                     if (!is_dir($this->inline_attachments) || !is_writable($this->inline_attachments)) {
                         // cannot write, skip
                         $info['warning'][] = 'attachment at ' . $frame_offset . ' cannot be saved to "' . $this->inline_attachments . '" (not writable)';
                         unset($parsedFrame['data']);
                         break;
                     }
                 }
                 // if we get this far, must be OK
                 if (is_string($this->inline_attachments)) {
                     $destination_filename = $this->inline_attachments . DIRECTORY_SEPARATOR . md5($info['filenamepath']) . '_' . $frame_offset;
                     if (!file_exists($destination_filename) || is_writable($destination_filename)) {
                         file_put_contents($destination_filename, $parsedFrame['data']);
                     } else {
                         $info['warning'][] = 'attachment at ' . $frame_offset . ' cannot be saved to "' . $destination_filename . '" (not writable)';
                     }
                     $parsedFrame['data_filename'] = $destination_filename;
                     unset($parsedFrame['data']);
                 } else {
                     if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
                         if (!isset($info['id3v2']['comments']['picture'])) {
                             $info['id3v2']['comments']['picture'] = array();
                         }
                         $info['id3v2']['comments']['picture'][] = array('data' => $parsedFrame['data'], 'image_mime' => $parsedFrame['image_mime']);
                     }
                 }
             } while (false);
         }
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GEOB' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'GEO') {
         // 4.16  GEO  General encapsulated object
         //   There may be more than one 'GEOB' frame in each tag,
         //   but only one with the same content descriptor
         // <Header for 'General encapsulated object', ID: 'GEOB'>
         // Text encoding          $xx
         // MIME type              <text string> $00
         // Filename               <text string according to encoding> $00 (00)
         // Content description    <text string according to encoding> $00 (00)
         // Encapsulated object    <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_mimetype) === 0) {
             $frame_mimetype = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_filename = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_filename) === 0) {
             $frame_filename = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $parsedFrame['objectdata'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['filename'] = $frame_filename;
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PCNT' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CNT') {
         // 4.17  CNT  Play counter
         //   There may only be one 'PCNT' frame in each tag.
         //   When the counter reaches all one's, one byte is inserted in
         //   front of the counter thus making the counter eight bits bigger
         // <Header for 'Play counter', ID: 'PCNT'>
         // Counter        $xx xx xx xx (xx ...)
         $parsedFrame['data'] = Helper::BigEndian2Int($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POPM' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'POP') {
         // 4.18  POP  Popularimeter
         //   There may be more than one 'POPM' frame in each tag,
         //   but only one with the same email address
         // <Header for 'Popularimeter', ID: 'POPM'>
         // Email to user   <text string> $00
         // Rating          $xx
         // Counter         $xx xx xx xx (xx ...)
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_emailaddress = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_emailaddress) === 0) {
             $frame_emailaddress = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rating = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['counter'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         $parsedFrame['email'] = $frame_emailaddress;
         $parsedFrame['rating'] = $frame_rating;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RBUF' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'BUF') {
         // 4.19  BUF  Recommended buffer size
         //   There may only be one 'RBUF' frame in each tag
         // <Header for 'Recommended buffer size', ID: 'RBUF'>
         // Buffer size               $xx xx xx
         // Embedded info flag        %0000000x
         // Offset to next tag        $xx xx xx xx
         $frame_offset = 0;
         $parsedFrame['buffersize'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 3));
         $frame_offset += 3;
         $frame_embeddedinfoflags = Helper::BigEndian2Bin(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['flags']['embededinfo'] = (bool) substr($frame_embeddedinfoflags, 7, 1);
         $parsedFrame['nexttagoffset'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRM') {
         // 4.20  Encrypted meta frame (ID3v2.2 only)
         //   There may be more than one 'CRM' frame in a tag,
         //   but only one with the same 'owner identifier'
         // <Header for 'Encrypted meta frame', ID: 'CRM'>
         // Owner identifier      <textstring> $00 (00)
         // Content/explanation   <textstring> $00 (00)
         // Encrypted datablock   <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['description'] = $frame_description;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'AENC' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'CRA') {
         // 4.21  CRA  Audio encryption
         //   There may be more than one 'AENC' frames in a tag,
         //   but only one with the same 'Owner identifier'
         // <Header for 'Audio encryption', ID: 'AENC'>
         // Owner identifier   <text string> $00
         // Preview start      $xx xx
         // Preview length     $xx xx
         // Encryption info    <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid == '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['previewstart'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['previewlength'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['encryptioninfo'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'LINK' || $id3v2_majorversion == 2 && $parsedFrame['frame_name'] == 'LNK') {
         // 4.22  LNK  Linked information
         //   There may be more than one 'LINK' frame in a tag,
         //   but only one with the same contents
         // <Header for 'Linked information', ID: 'LINK'>
         // ID3v2.3+ => Frame identifier   $xx xx xx xx
         // ID3v2.2  => Frame identifier   $xx xx xx
         // URL                            <text string> $00
         // ID and additional data         <text string(s)>
         $frame_offset = 0;
         if ($id3v2_majorversion == 2) {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 3);
             $frame_offset += 3;
         } else {
             $parsedFrame['frameid'] = substr($parsedFrame['data'], $frame_offset, 4);
             $frame_offset += 4;
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_url = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_url) === 0) {
             $frame_url = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['url'] = $frame_url;
         $parsedFrame['additionaldata'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && $parsedFrame['url']) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = utf8_encode($parsedFrame['url']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'POSS') {
         // 4.21  POSS Position synchronisation frame (ID3v2.3+ only)
         //   There may only be one 'POSS' frame in each tag
         // <Head for 'Position synchronisation', ID: 'POSS'>
         // Time stamp format         $xx
         // Position                  $xx (xx ...)
         $frame_offset = 0;
         $parsedFrame['timestampformat'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['position'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset));
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'USER') {
         // 4.22  USER Terms of use (ID3v2.3+ only)
         //   There may be more than one 'Terms of use' frame in a tag,
         //   but only one with the same 'Language'
         // <Header for 'Terms of use frame', ID: 'USER'>
         // Text encoding        $xx
         // Language             $xx xx xx
         // The actual text      <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_language = substr($parsedFrame['data'], $frame_offset, 3);
         $frame_offset += 3;
         $parsedFrame['language'] = $frame_language;
         $parsedFrame['languagename'] = $this->LanguageLookup($frame_language, false);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
         if (!empty($parsedFrame['framenameshort']) && !empty($parsedFrame['data'])) {
             $info['id3v2']['comments'][$parsedFrame['framenameshort']][] = Helper::iconv_fallback($parsedFrame['encoding'], $info['id3v2']['encoding'], $parsedFrame['data']);
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'OWNE') {
         // 4.23  OWNE Ownership frame (ID3v2.3+ only)
         //   There may only be one 'OWNE' frame in a tag
         // <Header for 'Ownership frame', ID: 'OWNE'>
         // Text encoding     $xx
         // Price paid        <text string> $00
         // Date of purch.    <text string>
         // Seller            <text string according to encoding>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricepaid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['pricepaid']['currencyid'] = substr($frame_pricepaid, 0, 3);
         $parsedFrame['pricepaid']['currency'] = $this->LookupCurrencyUnits($parsedFrame['pricepaid']['currencyid']);
         $parsedFrame['pricepaid']['value'] = substr($frame_pricepaid, 3);
         $parsedFrame['purchasedate'] = substr($parsedFrame['data'], $frame_offset, 8);
         if (!$this->IsValidDateStampString($parsedFrame['purchasedate'])) {
             $parsedFrame['purchasedateunix'] = mktime(0, 0, 0, substr($parsedFrame['purchasedate'], 4, 2), substr($parsedFrame['purchasedate'], 6, 2), substr($parsedFrame['purchasedate'], 0, 4));
         }
         $frame_offset += 8;
         $parsedFrame['seller'] = (string) substr($parsedFrame['data'], $frame_offset);
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'COMR') {
         // 4.24  COMR Commercial frame (ID3v2.3+ only)
         //   There may be more than one 'commercial frame' in a tag,
         //   but no two may be identical
         // <Header for 'Commercial frame', ID: 'COMR'>
         // Text encoding      $xx
         // Price string       <text string> $00
         // Valid until        <text string>
         // Contact URL        <text string> $00
         // Received as        $xx
         // Name of seller     <text string according to encoding> $00 (00)
         // Description        <text string according to encoding> $00 (00)
         // Picture MIME type  <string> $00
         // Seller logo        <binary data>
         $frame_offset = 0;
         $frame_textencoding = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         if ($id3v2_majorversion <= 3 && $frame_textencoding > 1 || $id3v2_majorversion == 4 && $frame_textencoding > 3) {
             $info['warning'][] = 'Invalid text encoding byte (' . $frame_textencoding . ') in frame "' . $parsedFrame['frame_name'] . '" - defaulting to ISO-8859-1 encoding';
         }
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_pricestring = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_rawpricearray = explode('/', $frame_pricestring);
         foreach ($frame_rawpricearray as $key => $val) {
             $frame_currencyid = substr($val, 0, 3);
             $parsedFrame['price'][$frame_currencyid]['currency'] = $this->LookupCurrencyUnits($frame_currencyid);
             $parsedFrame['price'][$frame_currencyid]['value'] = substr($val, 3);
         }
         $frame_datestring = substr($parsedFrame['data'], $frame_offset, 8);
         $frame_offset += 8;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_contacturl = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_receivedasid = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_sellername = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_sellername) === 0) {
             $frame_sellername = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = strpos($parsedFrame['data'], $this->TextEncodingTerminatorLookup($frame_textencoding), $frame_offset);
         if (ord(substr($parsedFrame['data'], $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding)), 1)) === 0) {
             ++$frame_terminatorpos;
             // strpos() fooled because 2nd byte of Unicode chars are often 0x00
         }
         $frame_description = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_description) === 0) {
             $frame_description = '';
         }
         $frame_offset = $frame_terminatorpos + strlen($this->TextEncodingTerminatorLookup($frame_textencoding));
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_mimetype = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         $frame_offset = $frame_terminatorpos + strlen("");
         $frame_sellerlogo = substr($parsedFrame['data'], $frame_offset);
         $parsedFrame['encodingid'] = $frame_textencoding;
         $parsedFrame['encoding'] = $this->TextEncodingNameLookup($frame_textencoding);
         $parsedFrame['pricevaliduntil'] = $frame_datestring;
         $parsedFrame['contacturl'] = $frame_contacturl;
         $parsedFrame['receivedasid'] = $frame_receivedasid;
         $parsedFrame['receivedas'] = $this->COMRReceivedAsLookup($frame_receivedasid);
         $parsedFrame['sellername'] = $frame_sellername;
         $parsedFrame['description'] = $frame_description;
         $parsedFrame['mime'] = $frame_mimetype;
         $parsedFrame['logo'] = $frame_sellerlogo;
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'ENCR') {
         // 4.25  ENCR Encryption method registration (ID3v2.3+ only)
         //   There may be several 'ENCR' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Encryption method registration', ID: 'ENCR'>
         // Owner identifier    <text string> $00
         // Method symbol       $xx
         // Encryption data     <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['methodsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'GRID') {
         // 4.26  GRID Group identification registration (ID3v2.3+ only)
         //   There may be several 'GRID' frames in a tag,
         //   but only one containing the same symbol
         //   and only one containing the same owner identifier
         // <Header for 'Group ID registration', ID: 'GRID'>
         // Owner identifier      <text string> $00
         // Group symbol          $xx
         // Group dependent data  <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'PRIV') {
         // 4.27  PRIV Private frame (ID3v2.3+ only)
         //   The tag may contain more than one 'PRIV' frame
         //   but only with different contents
         // <Header for 'Private frame', ID: 'PRIV'>
         // Owner identifier      <text string> $00
         // The private data      <binary data>
         $frame_offset = 0;
         $frame_terminatorpos = strpos($parsedFrame['data'], "", $frame_offset);
         $frame_ownerid = substr($parsedFrame['data'], $frame_offset, $frame_terminatorpos - $frame_offset);
         if (ord($frame_ownerid) === 0) {
             $frame_ownerid = '';
         }
         $frame_offset = $frame_terminatorpos + strlen("");
         $parsedFrame['ownerid'] = $frame_ownerid;
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SIGN') {
         // 4.28  SIGN Signature frame (ID3v2.4+ only)
         //   There may be more than one 'signature frame' in a tag,
         //   but no two may be identical
         // <Header for 'Signature frame', ID: 'SIGN'>
         // Group symbol      $xx
         // Signature         <binary data>
         $frame_offset = 0;
         $parsedFrame['groupsymbol'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $parsedFrame['data'] = (string) substr($parsedFrame['data'], $frame_offset);
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'SEEK') {
         // 4.29  SEEK Seek frame (ID3v2.4+ only)
         //   There may only be one 'seek frame' in a tag
         // <Header for 'Seek frame', ID: 'SEEK'>
         // Minimum offset to next tag       $xx xx xx xx
         $frame_offset = 0;
         $parsedFrame['data'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
     } elseif ($id3v2_majorversion >= 4 && $parsedFrame['frame_name'] == 'ASPI') {
         // 4.30  ASPI Audio seek point index (ID3v2.4+ only)
         //   There may only be one 'audio seek point index' frame in a tag
         // <Header for 'Seek Point Index', ID: 'ASPI'>
         // Indexed data start (S)         $xx xx xx xx
         // Indexed data length (L)        $xx xx xx xx
         // Number of index points (N)     $xx xx
         // Bits per index point (b)       $xx
         //   Then for every index point the following data is included:
         // Fraction at index (Fi)          $xx (xx)
         $frame_offset = 0;
         $parsedFrame['datastart'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexeddatalength'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $parsedFrame['indexpoints'] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['bitsperpoint'] = ord(substr($parsedFrame['data'], $frame_offset++, 1));
         $frame_bytesperpoint = ceil($parsedFrame['bitsperpoint'] / 8);
         for ($i = 0; $i < $frame_indexpoints; ++$i) {
             $parsedFrame['indexes'][$i] = Helper::BigEndian2Int(substr($parsedFrame['data'], $frame_offset, $frame_bytesperpoint));
             $frame_offset += $frame_bytesperpoint;
         }
         unset($parsedFrame['data']);
     } elseif ($id3v2_majorversion >= 3 && $parsedFrame['frame_name'] == 'RGAD') {
         // Replay Gain Adjustment
         // http://privatewww.essex.ac.uk/~djmrob/replaygain/file_format_id3v2.html
         //   There may only be one 'RGAD' frame in a tag
         // <Header for 'Replay Gain Adjustment', ID: 'RGAD'>
         // Peak Amplitude                      $xx $xx $xx $xx
         // Radio Replay Gain Adjustment        %aaabbbcd %dddddddd
         // Audiophile Replay Gain Adjustment   %aaabbbcd %dddddddd
         //   a - name code
         //   b - originator code
         //   c - sign bit
         //   d - replay gain adjustment
         $frame_offset = 0;
         $parsedFrame['peakamplitude'] = Helper::BigEndian2Float(substr($parsedFrame['data'], $frame_offset, 4));
         $frame_offset += 4;
         $rg_track_adjustment = Helper::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $rg_album_adjustment = Helper::Dec2Bin(substr($parsedFrame['data'], $frame_offset, 2));
         $frame_offset += 2;
         $parsedFrame['raw']['track']['name'] = Helper::Bin2Dec(substr($rg_track_adjustment, 0, 3));
         $parsedFrame['raw']['track']['originator'] = Helper::Bin2Dec(substr($rg_track_adjustment, 3, 3));
         $parsedFrame['raw']['track']['signbit'] = Helper::Bin2Dec(substr($rg_track_adjustment, 6, 1));
         $parsedFrame['raw']['track']['adjustment'] = Helper::Bin2Dec(substr($rg_track_adjustment, 7, 9));
         $parsedFrame['raw']['album']['name'] = Helper::Bin2Dec(substr($rg_album_adjustment, 0, 3));
         $parsedFrame['raw']['album']['originator'] = Helper::Bin2Dec(substr($rg_album_adjustment, 3, 3));
         $parsedFrame['raw']['album']['signbit'] = Helper::Bin2Dec(substr($rg_album_adjustment, 6, 1));
         $parsedFrame['raw']['album']['adjustment'] = Helper::Bin2Dec(substr($rg_album_adjustment, 7, 9));
         $parsedFrame['track']['name'] = Helper::RGADnameLookup($parsedFrame['raw']['track']['name']);
         $parsedFrame['track']['originator'] = Helper::RGADoriginatorLookup($parsedFrame['raw']['track']['originator']);
         $parsedFrame['track']['adjustment'] = Helper::RGADadjustmentLookup($parsedFrame['raw']['track']['adjustment'], $parsedFrame['raw']['track']['signbit']);
         $parsedFrame['album']['name'] = Helper::RGADnameLookup($parsedFrame['raw']['album']['name']);
         $parsedFrame['album']['originator'] = Helper::RGADoriginatorLookup($parsedFrame['raw']['album']['originator']);
         $parsedFrame['album']['adjustment'] = Helper::RGADadjustmentLookup($parsedFrame['raw']['album']['adjustment'], $parsedFrame['raw']['album']['signbit']);
         $info['replay_gain']['track']['peak'] = $parsedFrame['peakamplitude'];
         $info['replay_gain']['track']['originator'] = $parsedFrame['track']['originator'];
         $info['replay_gain']['track']['adjustment'] = $parsedFrame['track']['adjustment'];
         $info['replay_gain']['album']['originator'] = $parsedFrame['album']['originator'];
         $info['replay_gain']['album']['adjustment'] = $parsedFrame['album']['adjustment'];
         unset($parsedFrame['data']);
     }
     return true;
 }
コード例 #12
0
ファイル: Swf.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'swf';
     $info['video']['dataformat'] = 'swf';
     // http://www.openswf.org/spec/SWFfileformat.html
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $SWFfileData = fread($this->getid3->fp, $info['avdataend'] - $info['avdataoffset']);
     // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
     $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
     switch ($info['swf']['header']['signature']) {
         case 'FWS':
             $info['swf']['header']['compressed'] = false;
             break;
         case 'CWS':
             $info['swf']['header']['compressed'] = true;
             break;
         default:
             $info['error'][] = 'Expecting "FWS" or "CWS" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($info['swf']['header']['signature']) . '"';
             unset($info['swf']);
             unset($info['fileformat']);
             return false;
             break;
     }
     $info['swf']['header']['version'] = Helper::LittleEndian2Int(substr($SWFfileData, 3, 1));
     $info['swf']['header']['length'] = Helper::LittleEndian2Int(substr($SWFfileData, 4, 4));
     if ($info['swf']['header']['compressed']) {
         $SWFHead = substr($SWFfileData, 0, 8);
         $SWFfileData = substr($SWFfileData, 8);
         if ($decompressed = @gzuncompress($SWFfileData)) {
             $SWFfileData = $SWFHead . $decompressed;
         } else {
             $info['error'][] = 'Error decompressing compressed SWF data (' . strlen($SWFfileData) . ' bytes compressed, should be ' . ($info['swf']['header']['length'] - 8) . ' bytes uncompressed)';
             return false;
         }
     }
     $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xf8) >> 3;
     $FrameSizeDataLength = ceil((5 + 4 * $FrameSizeBitsPerValue) / 8);
     $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x7), 3, '0', STR_PAD_LEFT);
     for ($i = 1; $i < $FrameSizeDataLength; ++$i) {
         $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
     }
     list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
     $info['swf']['header']['frame_width'] = Helper::Bin2Dec($X2);
     $info['swf']['header']['frame_height'] = Helper::Bin2Dec($Y2);
     // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
     // Next in the header is the frame rate, which is kind of weird.
     // It is supposed to be stored as a 16bit integer, but the first byte
     // (or last depending on how you look at it) is completely ignored.
     // Example: 0x000C  ->  0x0C  ->  12     So the frame rate is 12 fps.
     // Byte at (8 + $FrameSizeDataLength) is always zero and ignored
     $info['swf']['header']['frame_rate'] = Helper::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
     $info['swf']['header']['frame_count'] = Helper::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
     $info['video']['frame_rate'] = $info['swf']['header']['frame_rate'];
     $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20));
     $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20));
     $info['video']['pixel_aspect_ratio'] = (double) 1;
     if ($info['swf']['header']['frame_count'] > 0 && $info['swf']['header']['frame_rate'] > 0) {
         $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate'];
     }
     //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
     // SWF tags
     $CurrentOffset = 12 + $FrameSizeDataLength;
     $SWFdataLength = strlen($SWFfileData);
     while ($CurrentOffset < $SWFdataLength) {
         //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
         $TagIDTagLength = Helper::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
         $TagID = ($TagIDTagLength & 0xfffc) >> 6;
         $TagLength = $TagIDTagLength & 0x3f;
         $CurrentOffset += 2;
         if ($TagLength == 0x3f) {
             $TagLength = Helper::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
             $CurrentOffset += 4;
         }
         unset($TagData);
         $TagData['offset'] = $CurrentOffset;
         $TagData['size'] = $TagLength;
         $TagData['id'] = $TagID;
         $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
         switch ($TagID) {
             case 0:
                 // end of movie
                 break 2;
             case 9:
                 // Set background color
                 //$info['swf']['tags'][] = $TagData;
                 $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(Helper::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
                 break;
             default:
                 if ($this->ReturnAllTagData) {
                     $info['swf']['tags'][] = $TagData;
                 }
                 break;
         }
         $CurrentOffset += $TagLength;
     }
     return true;
 }
コード例 #13
0
ファイル: Flac.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type    $Block
  *
  * @return bool
  */
 public function parsePICTURE($Block = '')
 {
     $info =& $this->getid3->info;
     $picture['typeid'] = Helper::BigEndian2Int($this->fread(4));
     $picture['type'] = self::pictureTypeLookup($picture['typeid']);
     $picture['image_mime'] = $this->fread(Helper::BigEndian2Int($this->fread(4)));
     $descr_length = Helper::BigEndian2Int($this->fread(4));
     if ($descr_length) {
         $picture['description'] = $this->fread($descr_length);
     }
     $picture['width'] = Helper::BigEndian2Int($this->fread(4));
     $picture['height'] = Helper::BigEndian2Int($this->fread(4));
     $picture['color_depth'] = Helper::BigEndian2Int($this->fread(4));
     $picture['colors_indexed'] = Helper::BigEndian2Int($this->fread(4));
     $data_length = Helper::BigEndian2Int($this->fread(4));
     if ($picture['image_mime'] == '-->') {
         $picture['data'] = $this->fread($data_length);
     } else {
         $this->saveAttachment($picture['data'], $picture['type'] . '_' . $this->ftell() . '.' . substr($picture['image_mime'], 6), $this->ftell(), $data_length);
     }
     $info['flac']['PICTURE'][] = $picture;
     return true;
 }
コード例 #14
0
ファイル: Avr.php プロジェクト: Nattpyre/rocketfiles
 public function analyze()
 {
     $info =& $this->getid3->info;
     // http://cui.unige.ch/OSG/info/AudioFormats/ap11.html
     // http://www.btinternet.com/~AnthonyJ/Atari/programming/avr_format.html
     // offset    type    length    name        comments
     // ---------------------------------------------------------------------
     // 0    char    4    ID        format ID == "2BIT"
     // 4    char    8    name        sample name (unused space filled with 0)
     // 12    short    1    mono/stereo    0=mono, -1 (0xFFFF)=stereo
     //                     With stereo, samples are alternated,
     //                     the first voice is the left :
     //                     (LRLRLRLRLRLRLRLRLR...)
     // 14    short    1    resolution    8, 12 or 16 (bits)
     // 16    short    1    signed or not    0=unsigned, -1 (0xFFFF)=signed
     // 18    short    1    loop or not    0=no loop, -1 (0xFFFF)=loop on
     // 20    short    1    MIDI note    0xFFnn, where 0 <= nn <= 127
     //                     0xFFFF means "no MIDI note defined"
     // 22    byte    1    Replay speed    Frequence in the Replay software
     //                     0=5.485 Khz, 1=8.084 Khz, 2=10.971 Khz,
     //                     3=16.168 Khz, 4=21.942 Khz, 5=32.336 Khz
     //                     6=43.885 Khz, 7=47.261 Khz
     //                     -1 (0xFF)=no defined Frequence
     // 23    byte    3    sample rate    in Hertz
     // 26    long    1    size in bytes (2 * bytes in stereo)
     // 30    long    1    loop begin    0 for no loop
     // 34    long    1    loop size    equal to 'size' for no loop
     // 38  short   2   Reserved, MIDI keyboard split */
     // 40  short   2   Reserved, sample compression */
     // 42  short   2   Reserved */
     // 44  char   20;  Additional filename space, used if (name[7] != 0)
     // 64    byte    64    user data
     // 128    bytes    ?    sample data    (12 bits samples are coded on 16 bits:
     //                     0000 xxxx xxxx xxxx)
     // ---------------------------------------------------------------------
     // Note that all values are in motorola (big-endian) format, and that long is
     // assumed to be 4 bytes, and short 2 bytes.
     // When reading the samples, you should handle both signed and unsigned data,
     // and be prepared to convert 16->8 bit, or mono->stereo if needed. To convert
     // 8-bit data between signed/unsigned just add 127 to the sample values.
     // Simularly for 16-bit data you should add 32769
     $info['fileformat'] = 'avr';
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $AVRheader = fread($this->getid3->fp, 128);
     $info['avr']['raw']['magic'] = substr($AVRheader, 0, 4);
     $magic = '2BIT';
     if ($info['avr']['raw']['magic'] != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($info['avr']['raw']['magic']) . '"';
         unset($info['fileformat']);
         unset($info['avr']);
         return false;
     }
     $info['avdataoffset'] += 128;
     $info['avr']['sample_name'] = rtrim(substr($AVRheader, 4, 8));
     $info['avr']['raw']['mono'] = Helper::BigEndian2Int(substr($AVRheader, 12, 2));
     $info['avr']['bits_per_sample'] = Helper::BigEndian2Int(substr($AVRheader, 14, 2));
     $info['avr']['raw']['signed'] = Helper::BigEndian2Int(substr($AVRheader, 16, 2));
     $info['avr']['raw']['loop'] = Helper::BigEndian2Int(substr($AVRheader, 18, 2));
     $info['avr']['raw']['midi'] = Helper::BigEndian2Int(substr($AVRheader, 20, 2));
     $info['avr']['raw']['replay_freq'] = Helper::BigEndian2Int(substr($AVRheader, 22, 1));
     $info['avr']['sample_rate'] = Helper::BigEndian2Int(substr($AVRheader, 23, 3));
     $info['avr']['sample_length'] = Helper::BigEndian2Int(substr($AVRheader, 26, 4));
     $info['avr']['loop_start'] = Helper::BigEndian2Int(substr($AVRheader, 30, 4));
     $info['avr']['loop_end'] = Helper::BigEndian2Int(substr($AVRheader, 34, 4));
     $info['avr']['midi_split'] = Helper::BigEndian2Int(substr($AVRheader, 38, 2));
     $info['avr']['sample_compression'] = Helper::BigEndian2Int(substr($AVRheader, 40, 2));
     $info['avr']['reserved'] = Helper::BigEndian2Int(substr($AVRheader, 42, 2));
     $info['avr']['sample_name_extra'] = rtrim(substr($AVRheader, 44, 20));
     $info['avr']['comment'] = rtrim(substr($AVRheader, 64, 64));
     $info['avr']['flags']['stereo'] = $info['avr']['raw']['mono'] == 0 ? false : true;
     $info['avr']['flags']['signed'] = $info['avr']['raw']['signed'] == 0 ? false : true;
     $info['avr']['flags']['loop'] = $info['avr']['raw']['loop'] == 0 ? false : true;
     $info['avr']['midi_notes'] = array();
     if (($info['avr']['raw']['midi'] & 0xff00) != 0xff00) {
         $info['avr']['midi_notes'][] = ($info['avr']['raw']['midi'] & 0xff00) >> 8;
     }
     if (($info['avr']['raw']['midi'] & 0xff) != 0xff) {
         $info['avr']['midi_notes'][] = $info['avr']['raw']['midi'] & 0xff;
     }
     if ($info['avdataend'] - $info['avdataoffset'] != $info['avr']['sample_length'] * ($info['avr']['bits_per_sample'] == 8 ? 1 : 2)) {
         $info['warning'][] = 'Probable truncated file: expecting ' . $info['avr']['sample_length'] * ($info['avr']['bits_per_sample'] == 8 ? 1 : 2) . ' bytes of audio data, found ' . ($info['avdataend'] - $info['avdataoffset']);
     }
     $info['audio']['dataformat'] = 'avr';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'cbr';
     $info['audio']['bits_per_sample'] = $info['avr']['bits_per_sample'];
     $info['audio']['sample_rate'] = $info['avr']['sample_rate'];
     $info['audio']['channels'] = $info['avr']['flags']['stereo'] ? 2 : 1;
     $info['playtime_seconds'] = $info['avr']['sample_length'] / $info['audio']['channels'] / $info['avr']['sample_rate'];
     $info['audio']['bitrate'] = $info['avr']['sample_length'] * ($info['avr']['bits_per_sample'] == 8 ? 8 : 16) / $info['playtime_seconds'];
     return true;
 }
コード例 #15
0
ファイル: Aac.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @staticvar array $decbin
  *
  * @param  type    $MaxFramesToScan
  * @param  type    $ReturnExtendedInfo
  *
  * @return bool
  */
 public function getAACADTSheaderFilepointer($MaxFramesToScan = 1000000, $ReturnExtendedInfo = false)
 {
     $info =& $this->getid3->info;
     // based loosely on code from AACfile by Jurgen Faul  <jfaulØgmx.de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
     // http://wiki.multimedia.cx/index.php?title=ADTS
     // * ADTS Fixed Header: these don't change from frame to frame
     // syncword                                       12    always: '111111111111'
     // ID                                              1    0: MPEG-4, 1: MPEG-2
     // MPEG layer                                      2    If you send AAC in MPEG-TS, set to 0
     // protection_absent                               1    0: CRC present; 1: no CRC
     // profile                                         2    0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
     // sampling_frequency_index                        4    15 not allowed
     // private_bit                                     1    usually 0
     // channel_configuration                           3
     // original/copy                                   1    0: original; 1: copy
     // home                                            1    usually 0
     // emphasis                                        2    only if ID == 0 (ie MPEG-4)  // not present in some documentation?
     // * ADTS Variable Header: these can change from frame to frame
     // copyright_identification_bit                    1
     // copyright_identification_start                  1
     // aac_frame_length                               13    length of the frame including header (in bytes)
     // adts_buffer_fullness                           11    0x7FF indicates VBR
     // no_raw_data_blocks_in_frame                     2
     // * ADTS Error check
     // crc_check                                      16    only if protection_absent == 0
     $byteoffset = $info['avdataoffset'];
     $framenumber = 0;
     // Init bit pattern array
     static $decbin = array();
     // Populate $bindec
     for ($i = 0; $i < 256; ++$i) {
         $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
     }
     // used to calculate bitrate below
     $BitrateCache = array();
     while (true) {
         // breaks out when end-of-file encountered, or invalid data found,
         // or MaxFramesToScan frames have been scanned
         if (!Helper::intValueSupported($byteoffset)) {
             $info['warning'][] = 'Unable to parse AAC file beyond ' . ftell($this->getid3->fp) . ' (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
             return false;
         }
         fseek($this->getid3->fp, $byteoffset, SEEK_SET);
         // First get substring
         $substring = fread($this->getid3->fp, 9);
         // header is 7 bytes (or 9 if CRC is present)
         $substringlength = strlen($substring);
         if ($substringlength != 9) {
             $info['error'][] = 'Failed to read 7 bytes at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (only read ' . $substringlength . ' bytes)';
             return false;
         }
         // this would be easier with 64-bit math, but split it up to allow for 32-bit:
         $header1 = Helper::BigEndian2Int(substr($substring, 0, 2));
         $header2 = Helper::BigEndian2Int(substr($substring, 2, 4));
         $header3 = Helper::BigEndian2Int(substr($substring, 6, 1));
         $info['aac']['header']['raw']['syncword'] = ($header1 & 0xfff0) >> 4;
         if ($info['aac']['header']['raw']['syncword'] != 0xfff) {
             $info['error'][] = 'Synch pattern (0x0FFF) not found at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (found 0x0' . strtoupper(dechex($info['aac']['header']['raw']['syncword'])) . ' instead)';
             //if ($info['fileformat'] == 'aac') {
             //	return true;
             //}
             unset($info['aac']);
             return false;
         }
         // Gather info for first frame only - this takes time to do 1000 times!
         if ($framenumber == 0) {
             $info['aac']['header_type'] = 'ADTS';
             $info['fileformat'] = 'aac';
             $info['audio']['dataformat'] = 'aac';
             $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x8) >> 3;
             $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x6) >> 1;
             $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x1) >> 0;
             $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xc0000000) >> 30;
             $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3c000000) >> 26;
             $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x2000000) >> 25;
             $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x1c00000) >> 22;
             $info['aac']['header']['raw']['original'] = ($header2 & 0x200000) >> 21;
             $info['aac']['header']['raw']['home'] = ($header2 & 0x100000) >> 20;
             $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x80000) >> 19;
             $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x40000) >> 18;
             $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x3ffe0) >> 5;
             $info['aac']['header']['mpeg_version'] = $info['aac']['header']['raw']['mpeg_version'] ? 2 : 4;
             $info['aac']['header']['crc_present'] = $info['aac']['header']['raw']['protection_absent'] ? false : true;
             $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
             $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
             $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
             $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
             $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
             $info['aac']['header']['channels'] = $info['aac']['header']['raw']['channels_code'] == 7 ? 8 : $info['aac']['header']['raw']['channels_code'];
             if ($ReturnExtendedInfo) {
                 $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
                 $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
             }
             if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
                 $info['warning'][] = 'Layer error - expected "0", found "' . $info['aac']['header']['raw']['mpeg_layer'] . '" instead';
             }
             if ($info['aac']['header']['sample_frequency'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
                 return false;
             }
             $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
             $info['audio']['channels'] = $info['aac']['header']['channels'];
         }
         $FrameLength = ($header2 & 0x3ffe0) >> 5;
         if (!isset($BitrateCache[$FrameLength])) {
             $BitrateCache[$FrameLength] = $info['aac']['header']['sample_frequency'] / 1024 * $FrameLength * 8;
         }
         Helper::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
         $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
         $info['aac'][$framenumber]['adts_buffer_fullness'] = ($header2 & 0x1f) << 6 & ($header3 & 0xfc) >> 2;
         if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x7ff) {
             $info['audio']['bitrate_mode'] = 'vbr';
         } else {
             $info['audio']['bitrate_mode'] = 'cbr';
         }
         $info['aac'][$framenumber]['num_raw_data_blocks'] = ($header3 & 0x3) >> 0;
         if ($info['aac']['header']['crc_present']) {
             //$info['aac'][$framenumber]['crc'] = GetId3_lib::BigEndian2Int(substr($substring, 7, 2);
         }
         if (!$ReturnExtendedInfo) {
             unset($info['aac'][$framenumber]);
         }
         /*
         $rounded_precision = 5000;
         $info['aac']['bitrate_distribution_rounded'] = array();
         foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
             $rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
             getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
         }
         ksort($info['aac']['bitrate_distribution_rounded']);
         */
         $byteoffset += $FrameLength;
         if (++$framenumber < $MaxFramesToScan && $byteoffset + 10 < $info['avdataend']) {
             // keep scanning
         } else {
             $info['aac']['frames'] = $framenumber;
             $info['playtime_seconds'] = $info['avdataend'] / $byteoffset * ($framenumber * 1024 / $info['aac']['header']['sample_frequency']);
             // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
             if ($info['playtime_seconds'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
                 return false;
             }
             $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             ksort($info['aac']['bitrate_distribution']);
             $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile'];
             return true;
         }
     }
     // should never get here.
 }
コード例 #16
0
ファイル: Ts.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $TSheader = fread($this->getid3->fp, 19);
     $magic = "G";
     if (substr($TSheader, 0, 1) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at ' . $info['avdataoffset'] . ', found ' . Helper::PrintHexBytes(substr($TSheader, 0, 1)) . ' instead.';
         return false;
     }
     $info['fileformat'] = 'ts';
     // http://en.wikipedia.org/wiki/.ts
     $offset = 0;
     $info['ts']['packet']['sync'] = Helper::BigEndian2Int(substr($TSheader, $offset, 1));
     $offset += 1;
     $pid_flags_raw = Helper::BigEndian2Int(substr($TSheader, $offset, 2));
     $offset += 2;
     $SAC_raw = Helper::BigEndian2Int(substr($TSheader, $offset, 1));
     $offset += 1;
     $info['ts']['packet']['flags']['transport_error_indicator'] = (bool) ($pid_flags_raw & 0x8000);
     // Set by demodulator if can't correct errors in the stream, to tell the demultiplexer that the packet has an uncorrectable error
     $info['ts']['packet']['flags']['payload_unit_start_indicator'] = (bool) ($pid_flags_raw & 0x4000);
     // 1 means start of PES data or PSI otherwise zero only.
     $info['ts']['packet']['flags']['transport_high_priority'] = (bool) ($pid_flags_raw & 0x2000);
     // 1 means higher priority than other packets with the same PID.
     $info['ts']['packet']['packet_id'] = ($pid_flags_raw & 0x1fff) >> 0;
     $info['ts']['packet']['raw']['scrambling_control'] = ($SAC_raw & 0xc0) >> 6;
     $info['ts']['packet']['flags']['adaption_field_exists'] = (bool) ($SAC_raw & 0x20);
     $info['ts']['packet']['flags']['payload_exists'] = (bool) ($SAC_raw & 0x10);
     $info['ts']['packet']['continuity_counter'] = ($SAC_raw & 0xf) >> 0;
     // Incremented only when a payload is present
     $info['ts']['packet']['scrambling_control'] = $this->TSscramblingControlLookup($info['ts']['packet']['raw']['scrambling_control']);
     if ($info['ts']['packet']['flags']['adaption_field_exists']) {
         $AdaptionField_raw = Helper::BigEndian2Int(substr($TSheader, $offset, 2));
         $offset += 2;
         $info['ts']['packet']['adaption']['field_length'] = ($AdaptionField_raw & 0xff00) >> 8;
         // Number of bytes in the adaptation field immediately following this byte
         $info['ts']['packet']['adaption']['flags']['discontinuity'] = (bool) ($AdaptionField_raw & 0x80);
         // Set to 1 if current TS packet is in a discontinuity state with respect to either the continuity counter or the program clock reference
         $info['ts']['packet']['adaption']['flags']['random_access'] = (bool) ($AdaptionField_raw & 0x40);
         // Set to 1 if the PES packet in this TS packet starts a video/audio sequence
         $info['ts']['packet']['adaption']['flags']['high_priority'] = (bool) ($AdaptionField_raw & 0x20);
         // 1 = higher priority
         $info['ts']['packet']['adaption']['flags']['pcr'] = (bool) ($AdaptionField_raw & 0x10);
         // 1 means adaptation field does contain a PCR field
         $info['ts']['packet']['adaption']['flags']['opcr'] = (bool) ($AdaptionField_raw & 0x8);
         // 1 means adaptation field does contain an OPCR field
         $info['ts']['packet']['adaption']['flags']['splice_point'] = (bool) ($AdaptionField_raw & 0x4);
         // 1 means presence of splice countdown field in adaptation field
         $info['ts']['packet']['adaption']['flags']['private_data'] = (bool) ($AdaptionField_raw & 0x2);
         // 1 means presence of private data bytes in adaptation field
         $info['ts']['packet']['adaption']['flags']['extension'] = (bool) ($AdaptionField_raw & 0x1);
         // 1 means presence of adaptation field extension
         if ($info['ts']['packet']['adaption']['flags']['pcr']) {
             $info['ts']['packet']['adaption']['raw']['pcr'] = Helper::BigEndian2Int(substr($TSheader, $offset, 6));
             $offset += 6;
         }
         if ($info['ts']['packet']['adaption']['flags']['opcr']) {
             $info['ts']['packet']['adaption']['raw']['opcr'] = Helper::BigEndian2Int(substr($TSheader, $offset, 6));
             $offset += 6;
         }
     }
     $info['error'][] = 'MPEG Transport Stream (.ts) parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']';
     return false;
 }
コード例 #17
0
ファイル: Tiff.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type    $bytestring
  * @param  type    $byteorder
  *
  * @return bool
  */
 public function TIFFendian2Int($bytestring, $byteorder)
 {
     if ($byteorder == 'Intel') {
         return Helper::LittleEndian2Int($bytestring);
     } elseif ($byteorder == 'Motorola') {
         return Helper::BigEndian2Int($bytestring);
     }
     return false;
 }
コード例 #18
0
ファイル: Riff.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type $byteword
  * @param  type $signed
  *
  * @return type
  */
 public function EitherEndian2Int($byteword, $signed = false)
 {
     if ($this->getid3->info['fileformat'] == 'riff') {
         return Helper::LittleEndian2Int($byteword, $signed);
     }
     return Helper::BigEndian2Int($byteword, false, $signed);
 }
コード例 #19
0
ファイル: Mpc.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @link http://trac.musepack.net/trac/wiki/SV8Specification
  */
 public function ParseMPCsv8()
 {
     // this is SV8
     $info =& $this->getid3->info;
     $thisfile_mpc_header =& $info['mpc']['header'];
     $keyNameSize = 2;
     $maxHandledPacketLength = 9;
     // specs say: "n*8; 0 < n < 10"
     $offset = ftell($this->getid3->fp);
     while ($offset < $info['avdataend']) {
         $thisPacket = array();
         $thisPacket['offset'] = $offset;
         $packet_offset = 0;
         // Size is a variable-size field, could be 1-4 bytes (possibly more?)
         // read enough data in and figure out the exact size later
         $MPCheaderData = fread($this->getid3->fp, $keyNameSize + $maxHandledPacketLength);
         $packet_offset += $keyNameSize;
         $thisPacket['key'] = substr($MPCheaderData, 0, $keyNameSize);
         $thisPacket['key_name'] = $this->MPCsv8PacketName($thisPacket['key']);
         if ($thisPacket['key'] == $thisPacket['key_name']) {
             $info['error'][] = 'Found unexpected key value "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset'];
             return false;
         }
         $packetLength = 0;
         $thisPacket['packet_size'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $keyNameSize), $packetLength);
         // includes keyname and packet_size field
         if ($thisPacket['packet_size'] === false) {
             $info['error'][] = 'Did not find expected packet length within ' . $maxHandledPacketLength . ' bytes at offset ' . ($thisPacket['offset'] + $keyNameSize);
             return false;
         }
         $packet_offset += $packetLength;
         $offset += $thisPacket['packet_size'];
         switch ($thisPacket['key']) {
             case 'SH':
                 // Stream Header
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
                 }
                 $thisPacket['crc'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 4));
                 $packet_offset += 4;
                 $thisPacket['stream_version'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $packetLength = 0;
                 $thisPacket['sample_count'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 $packetLength = 0;
                 $thisPacket['beginning_silence'] = $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 $otherUsefulData = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['sample_frequency_raw'] = ($otherUsefulData & 0xe000) >> 13;
                 $thisPacket['max_bands_used'] = ($otherUsefulData & 0x1f00) >> 8;
                 $thisPacket['channels'] = (($otherUsefulData & 0xf0) >> 4) + 1;
                 $thisPacket['ms_used'] = (bool) (($otherUsefulData & 0x8) >> 3);
                 $thisPacket['audio_block_frames'] = ($otherUsefulData & 0x7) >> 0;
                 $thisPacket['sample_frequency'] = $this->MPCfrequencyLookup($thisPacket['sample_frequency_raw']);
                 $thisfile_mpc_header['mid_side_stereo'] = $thisPacket['ms_used'];
                 $thisfile_mpc_header['sample_rate'] = $thisPacket['sample_frequency'];
                 $thisfile_mpc_header['samples'] = $thisPacket['sample_count'];
                 $thisfile_mpc_header['stream_version_major'] = $thisPacket['stream_version'];
                 $info['audio']['channels'] = $thisPacket['channels'];
                 $info['audio']['sample_rate'] = $thisPacket['sample_frequency'];
                 $info['playtime_seconds'] = $thisPacket['sample_count'] / $thisPacket['sample_frequency'];
                 $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
                 break;
             case 'RG':
                 // Replay Gain
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
                 }
                 $thisPacket['replaygain_version'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['replaygain_title_gain'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_title_peak'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_album_gain'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 $thisPacket['replaygain_album_peak'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 2));
                 $packet_offset += 2;
                 if ($thisPacket['replaygain_title_gain']) {
                     $info['replay_gain']['title']['gain'] = $thisPacket['replaygain_title_gain'];
                 }
                 if ($thisPacket['replaygain_title_peak']) {
                     $info['replay_gain']['title']['peak'] = $thisPacket['replaygain_title_peak'];
                 }
                 if ($thisPacket['replaygain_album_gain']) {
                     $info['replay_gain']['album']['gain'] = $thisPacket['replaygain_album_gain'];
                 }
                 if ($thisPacket['replaygain_album_peak']) {
                     $info['replay_gain']['album']['peak'] = $thisPacket['replaygain_album_peak'];
                 }
                 break;
             case 'EI':
                 // Encoder Info
                 $moreBytesToRead = $thisPacket['packet_size'] - $keyNameSize - $maxHandledPacketLength;
                 if ($moreBytesToRead > 0) {
                     $MPCheaderData .= fread($this->getid3->fp, $moreBytesToRead);
                 }
                 $profile_pns = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $quality_int = ($profile_pns & 0xf0) >> 4;
                 $quality_dec = ($profile_pns & 0xe) >> 3;
                 $thisPacket['quality'] = (double) $quality_int + $quality_dec / 8;
                 $thisPacket['pns_tool'] = (bool) (($profile_pns & 0x1) >> 0);
                 $thisPacket['version_major'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version_minor'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version_build'] = Helper::BigEndian2Int(substr($MPCheaderData, $packet_offset, 1));
                 $packet_offset += 1;
                 $thisPacket['version'] = $thisPacket['version_major'] . '.' . $thisPacket['version_minor'] . '.' . $thisPacket['version_build'];
                 $info['audio']['encoder'] = 'MPC v' . $thisPacket['version'] . ' (' . ($thisPacket['version_minor'] % 2 ? 'unstable' : 'stable') . ')';
                 $thisfile_mpc_header['encoder_version'] = $info['audio']['encoder'];
                 //$thisfile_mpc_header['quality']         = (float) ($thisPacket['quality'] / 1.5875); // values can range from 0.000 to 15.875, mapped to qualities of 0.0 to 10.0
                 $thisfile_mpc_header['quality'] = (double) ($thisPacket['quality'] - 5);
                 // values can range from 0.000 to 15.875, of which 0..4 are "reserved/experimental", and 5..15 are mapped to qualities of 0.0 to 10.0
                 break;
             case 'SO':
                 // Seek Table Offset
                 $packetLength = 0;
                 $thisPacket['seek_table_offset'] = $thisPacket['offset'] + $this->SV8variableLengthInteger(substr($MPCheaderData, $packet_offset, $maxHandledPacketLength), $packetLength);
                 $packet_offset += $packetLength;
                 break;
             case 'ST':
                 // Seek Table
             // Seek Table
             case 'SE':
                 // Stream End
             // Stream End
             case 'AP':
                 // Audio Data
                 // nothing useful here, just skip this packet
                 $thisPacket = array();
                 break;
             default:
                 $info['error'][] = 'Found unhandled key type "' . $thisPacket['key'] . '" at offset ' . $thisPacket['offset'];
                 return false;
                 break;
         }
         if (!empty($thisPacket)) {
             $info['mpc']['packets'][] = $thisPacket;
         }
         fseek($this->getid3->fp, $offset);
     }
     $thisfile_mpc_header['size'] = $offset;
     return true;
 }
コード例 #20
0
ファイル: Mp3.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type    $Header4Bytes
  *
  * @return bool
  */
 public static function MPEGaudioHeaderDecode($Header4Bytes)
 {
     // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
     // A - Frame sync (all bits set)
     // B - MPEG Audio version ID
     // C - Layer description
     // D - Protection bit
     // E - Bitrate index
     // F - Sampling rate frequency index
     // G - Padding bit
     // H - Private bit
     // I - Channel Mode
     // J - Mode extension (Only if Joint stereo)
     // K - Copyright
     // L - Original
     // M - Emphasis
     if (strlen($Header4Bytes) != 4) {
         return false;
     }
     $MPEGrawHeader['synch'] = (Helper::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xffe0) >> 4;
     $MPEGrawHeader['version'] = (ord($Header4Bytes[1]) & 0x18) >> 3;
     //    BB
     $MPEGrawHeader['layer'] = (ord($Header4Bytes[1]) & 0x6) >> 1;
     //      CC
     $MPEGrawHeader['protection'] = ord($Header4Bytes[1]) & 0x1;
     //        D
     $MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xf0) >> 4;
     // EEEE
     $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0xc) >> 2;
     //     FF
     $MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x2) >> 1;
     //       G
     $MPEGrawHeader['private'] = ord($Header4Bytes[2]) & 0x1;
     //        H
     $MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xc0) >> 6;
     // II
     $MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4;
     //   JJ
     $MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x8) >> 3;
     //     K
     $MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x4) >> 2;
     //      L
     $MPEGrawHeader['emphasis'] = ord($Header4Bytes[3]) & 0x3;
     //       MM
     return $MPEGrawHeader;
 }
コード例 #21
0
ファイル: Midi.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     // shortcut
     $info['midi']['raw'] = array();
     $thisfile_midi =& $info['midi'];
     $thisfile_midi_raw =& $thisfile_midi['raw'];
     $info['fileformat'] = 'midi';
     $info['audio']['dataformat'] = 'midi';
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $MIDIdata = fread($this->getid3->fp, $this->getid3->fread_buffer_size());
     $offset = 0;
     $MIDIheaderID = substr($MIDIdata, $offset, 4);
     // 'MThd'
     if ($MIDIheaderID != self::GETID3_MIDI_MAGIC_MTHD) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes(self::GETID3_MIDI_MAGIC_MTHD) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($MIDIheaderID) . '"';
         unset($info['fileformat']);
         return false;
     }
     $offset += 4;
     $thisfile_midi_raw['headersize'] = Helper::BigEndian2Int(substr($MIDIdata, $offset, 4));
     $offset += 4;
     $thisfile_midi_raw['fileformat'] = Helper::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     $thisfile_midi_raw['tracks'] = Helper::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     $thisfile_midi_raw['ticksperqnote'] = Helper::BigEndian2Int(substr($MIDIdata, $offset, 2));
     $offset += 2;
     for ($i = 0; $i < $thisfile_midi_raw['tracks']; ++$i) {
         while (strlen($MIDIdata) - $offset < 8) {
             $MIDIdata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size());
         }
         $trackID = substr($MIDIdata, $offset, 4);
         $offset += 4;
         if ($trackID == self::GETID3_MIDI_MAGIC_MTRK) {
             $tracksize = Helper::BigEndian2Int(substr($MIDIdata, $offset, 4));
             $offset += 4;
             // $thisfile_midi['tracks'][$i]['size'] = $tracksize;
             $trackdataarray[$i] = substr($MIDIdata, $offset, $tracksize);
             $offset += $tracksize;
         } else {
             $info['error'][] = 'Expecting "' . Helper::PrintHexBytes(self::GETID3_MIDI_MAGIC_MTRK) . '" at ' . ($offset - 4) . ', found "' . Helper::PrintHexBytes($trackID) . '" instead';
             return false;
         }
     }
     if (!isset($trackdataarray) || !is_array($trackdataarray)) {
         $info['error'][] = 'Cannot find MIDI track information';
         unset($thisfile_midi);
         unset($info['fileformat']);
         return false;
     }
     if ($this->scanwholefile) {
         // this can take quite a long time, so have the option to bypass it if speed is very important
         $thisfile_midi['totalticks'] = 0;
         $info['playtime_seconds'] = 0;
         $CurrentMicroSecondsPerBeat = 500000;
         // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
         $CurrentBeatsPerMinute = 120;
         // 120 beats per minute;  60,000,000 microseconds per minute -> 500,000 microseconds per beat
         $MicroSecondsPerQuarterNoteAfter = array();
         foreach ($trackdataarray as $tracknumber => $trackdata) {
             $eventsoffset = 0;
             $LastIssuedMIDIcommand = 0;
             $LastIssuedMIDIchannel = 0;
             $CumulativeDeltaTime = 0;
             $TicksAtCurrentBPM = 0;
             while ($eventsoffset < strlen($trackdata)) {
                 $eventid = 0;
                 if (isset($MIDIevents[$tracknumber]) && is_array($MIDIevents[$tracknumber])) {
                     $eventid = count($MIDIevents[$tracknumber]);
                 }
                 $deltatime = 0;
                 for ($i = 0; $i < 4; ++$i) {
                     $deltatimebyte = ord(substr($trackdata, $eventsoffset++, 1));
                     $deltatime = ($deltatime << 7) + ($deltatimebyte & 0x7f);
                     if ($deltatimebyte & 0x80) {
                         // another byte follows
                     } else {
                         break;
                     }
                 }
                 $CumulativeDeltaTime += $deltatime;
                 $TicksAtCurrentBPM += $deltatime;
                 $MIDIevents[$tracknumber][$eventid]['deltatime'] = $deltatime;
                 $MIDI_event_channel = ord(substr($trackdata, $eventsoffset++, 1));
                 if ($MIDI_event_channel & 0x80) {
                     // OK, normal event - MIDI command has MSB set
                     $LastIssuedMIDIcommand = $MIDI_event_channel >> 4;
                     $LastIssuedMIDIchannel = $MIDI_event_channel & 0xf;
                 } else {
                     // running event - assume last command
                     --$eventsoffset;
                 }
                 $MIDIevents[$tracknumber][$eventid]['eventid'] = $LastIssuedMIDIcommand;
                 $MIDIevents[$tracknumber][$eventid]['channel'] = $LastIssuedMIDIchannel;
                 if ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x8) {
                     // Note off (key is released)
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0x9) {
                     // Note on (key is pressed)
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xa) {
                     // Key after-touch
                     $notenumber = ord(substr($trackdata, $eventsoffset++, 1));
                     $velocity = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xb) {
                     // Control Change
                     $controllernum = ord(substr($trackdata, $eventsoffset++, 1));
                     $newvalue = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xc) {
                     // Program (patch) change
                     $newprogramnum = ord(substr($trackdata, $eventsoffset++, 1));
                     $thisfile_midi_raw['track'][$tracknumber]['instrumentid'] = $newprogramnum;
                     if ($tracknumber == 10) {
                         $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIpercussionLookup($newprogramnum);
                     } else {
                         $thisfile_midi_raw['track'][$tracknumber]['instrument'] = $this->GeneralMIDIinstrumentLookup($newprogramnum);
                     }
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xd) {
                     // Channel after-touch
                     $channelnumber = ord(substr($trackdata, $eventsoffset++, 1));
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xe) {
                     // Pitch wheel change (2000H is normal or no change)
                     $changeLSB = ord(substr($trackdata, $eventsoffset++, 1));
                     $changeMSB = ord(substr($trackdata, $eventsoffset++, 1));
                     $pitchwheelchange = ($changeMSB & 0x7f) << 7 & ($changeLSB & 0x7f);
                 } elseif ($MIDIevents[$tracknumber][$eventid]['eventid'] == 0xf && $MIDIevents[$tracknumber][$eventid]['channel'] == 0xf) {
                     $METAeventCommand = ord(substr($trackdata, $eventsoffset++, 1));
                     $METAeventLength = ord(substr($trackdata, $eventsoffset++, 1));
                     $METAeventData = substr($trackdata, $eventsoffset, $METAeventLength);
                     $eventsoffset += $METAeventLength;
                     switch ($METAeventCommand) {
                         case 0x0:
                             // Set track sequence number
                             $track_sequence_number = Helper::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['seqno'] = $track_sequence_number;
                             break;
                         case 0x1:
                             // Text: generic
                             $text_generic = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['text'] = $text_generic;
                             $thisfile_midi['comments']['comment'][] = $text_generic;
                             break;
                         case 0x2:
                             // Text: copyright
                             $text_copyright = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['copyright'] = $text_copyright;
                             $thisfile_midi['comments']['copyright'][] = $text_copyright;
                             break;
                         case 0x3:
                             // Text: track name
                             $text_trackname = substr($METAeventData, 0, $METAeventLength);
                             $thisfile_midi_raw['track'][$tracknumber]['name'] = $text_trackname;
                             break;
                         case 0x4:
                             // Text: track instrument name
                             $text_instrument = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['instrument'] = $text_instrument;
                             break;
                         case 0x5:
                             // Text: lyrics
                             $text_lyrics = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['lyrics'] = $text_lyrics;
                             if (!isset($thisfile_midi['lyrics'])) {
                                 $thisfile_midi['lyrics'] = '';
                             }
                             $thisfile_midi['lyrics'] .= $text_lyrics . "\n";
                             break;
                         case 0x6:
                             // Text: marker
                             $text_marker = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['marker'] = $text_marker;
                             break;
                         case 0x7:
                             // Text: cue point
                             $text_cuepoint = substr($METAeventData, 0, $METAeventLength);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['cuepoint'] = $text_cuepoint;
                             break;
                         case 0x2f:
                             // End Of Track
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['EOT'] = $CumulativeDeltaTime;
                             break;
                         case 0x51:
                             // Tempo: microseconds / quarter note
                             $CurrentMicroSecondsPerBeat = Helper::BigEndian2Int(substr($METAeventData, 0, $METAeventLength));
                             if ($CurrentMicroSecondsPerBeat == 0) {
                                 $info['error'][] = 'Corrupt MIDI file: CurrentMicroSecondsPerBeat == zero';
                                 return false;
                             }
                             $thisfile_midi_raw['events'][$tracknumber][$CumulativeDeltaTime]['us_qnote'] = $CurrentMicroSecondsPerBeat;
                             $CurrentBeatsPerMinute = 1000000 / $CurrentMicroSecondsPerBeat * 60;
                             $MicroSecondsPerQuarterNoteAfter[$CumulativeDeltaTime] = $CurrentMicroSecondsPerBeat;
                             $TicksAtCurrentBPM = 0;
                             break;
                         case 0x58:
                             // Time signature
                             $timesig_numerator = Helper::BigEndian2Int($METAeventData[0]);
                             $timesig_denominator = pow(2, Helper::BigEndian2Int($METAeventData[1]));
                             // $02 -> x/4, $03 -> x/8, etc
                             $timesig_32inqnote = Helper::BigEndian2Int($METAeventData[2]);
                             // number of 32nd notes to the quarter note
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_32inqnote']   = $timesig_32inqnote;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_numerator']   = $timesig_numerator;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_denominator'] = $timesig_denominator;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['timesig_text']        = $timesig_numerator.'/'.$timesig_denominator;
                             $thisfile_midi['timesignature'][] = $timesig_numerator . '/' . $timesig_denominator;
                             break;
                         case 0x59:
                             // Keysignature
                             $keysig_sharpsflats = Helper::BigEndian2Int($METAeventData[0]);
                             if ($keysig_sharpsflats & 0x80) {
                                 // (-7 -> 7 flats, 0 ->key of C, 7 -> 7 sharps)
                                 $keysig_sharpsflats -= 256;
                             }
                             $keysig_majorminor = Helper::BigEndian2Int($METAeventData[1]);
                             // 0 -> major, 1 -> minor
                             $keysigs = array(-7 => 'Cb', -6 => 'Gb', -5 => 'Db', -4 => 'Ab', -3 => 'Eb', -2 => 'Bb', -1 => 'F', 0 => 'C', 1 => 'G', 2 => 'D', 3 => 'A', 4 => 'E', 5 => 'B', 6 => 'F#', 7 => 'C#');
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_sharps'] = (($keysig_sharpsflats > 0) ? abs($keysig_sharpsflats) : 0);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_flats']  = (($keysig_sharpsflats < 0) ? abs($keysig_sharpsflats) : 0);
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor']  = (bool) $keysig_majorminor;
                             //$thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_text']   = $keysigs[$keysig_sharpsflats].' '.($thisfile_midi_raw['events'][$tracknumber][$eventid]['keysig_minor'] ? 'minor' : 'major');
                             // $keysigs[$keysig_sharpsflats] gets an int key (correct) - $keysigs["$keysig_sharpsflats"] gets a string key (incorrect)
                             $thisfile_midi['keysignature'][] = $keysigs[$keysig_sharpsflats] . ' ' . ((bool) $keysig_majorminor ? 'minor' : 'major');
                             break;
                         case 0x7f:
                             // Sequencer specific information
                             $custom_data = substr($METAeventData, 0, $METAeventLength);
                             break;
                         default:
                             $info['warning'][] = 'Unhandled META Event Command: ' . $METAeventCommand;
                             break;
                     }
                 } else {
                     $info['warning'][] = 'Unhandled MIDI Event ID: ' . $MIDIevents[$tracknumber][$eventid]['eventid'] . ' + Channel ID: ' . $MIDIevents[$tracknumber][$eventid]['channel'];
                 }
             }
             if ($tracknumber > 0 || count($trackdataarray) == 1) {
                 $thisfile_midi['totalticks'] = max($thisfile_midi['totalticks'], $CumulativeDeltaTime);
             }
         }
         $previoustickoffset = null;
         ksort($MicroSecondsPerQuarterNoteAfter);
         foreach ($MicroSecondsPerQuarterNoteAfter as $tickoffset => $microsecondsperbeat) {
             if (is_null($previoustickoffset)) {
                 $prevmicrosecondsperbeat = $microsecondsperbeat;
                 $previoustickoffset = $tickoffset;
                 continue;
             }
             if ($thisfile_midi['totalticks'] > $tickoffset) {
                 if ($thisfile_midi_raw['ticksperqnote'] == 0) {
                     $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
                     return false;
                 }
                 $info['playtime_seconds'] += ($tickoffset - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($prevmicrosecondsperbeat / 1000000);
                 $prevmicrosecondsperbeat = $microsecondsperbeat;
                 $previoustickoffset = $tickoffset;
             }
         }
         if ($thisfile_midi['totalticks'] > $previoustickoffset) {
             if ($thisfile_midi_raw['ticksperqnote'] == 0) {
                 $info['error'][] = 'Corrupt MIDI file: ticksperqnote == zero';
                 return false;
             }
             $info['playtime_seconds'] += ($thisfile_midi['totalticks'] - $previoustickoffset) / $thisfile_midi_raw['ticksperqnote'] * ($microsecondsperbeat / 1000000);
         }
     }
     if (!empty($info['playtime_seconds'])) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (!empty($thisfile_midi['lyrics'])) {
         $thisfile_midi['comments']['lyrics'][] = $thisfile_midi['lyrics'];
     }
     return true;
 }
コード例 #22
0
 /**
  * @return type
  */
 public function getBit()
 {
     $result = Helper::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> 7 - $this->currentBits & 0x1;
     $this->skipBits(1);
     return $result;
 }
コード例 #23
0
ファイル: Png.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     // shortcut
     $info['png'] = array();
     $thisfile_png =& $info['png'];
     $info['fileformat'] = 'png';
     $info['video']['dataformat'] = 'png';
     $info['video']['lossless'] = false;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $PNGfiledata = fread($this->getid3->fp, $this->getid3->fread_buffer_size());
     $offset = 0;
     $PNGidentifier = substr($PNGfiledata, $offset, 8);
     // $89 $50 $4E $47 $0D $0A $1A $0A
     $offset += 8;
     if ($PNGidentifier != "‰PNG\r\n\n") {
         $info['error'][] = 'First 8 bytes of file (' . Helper::PrintHexBytes($PNGidentifier) . ') did not match expected PNG identifier';
         unset($info['fileformat']);
         return false;
     }
     while (ftell($this->getid3->fp) - (strlen($PNGfiledata) - $offset) < $info['filesize']) {
         $chunk['data_length'] = Helper::BigEndian2Int(substr($PNGfiledata, $offset, 4));
         $offset += 4;
         while (strlen($PNGfiledata) - $offset < $chunk['data_length'] + 4 && ftell($this->getid3->fp) < $info['filesize']) {
             $PNGfiledata .= fread($this->getid3->fp, $this->getid3->fread_buffer_size());
         }
         $chunk['type_text'] = substr($PNGfiledata, $offset, 4);
         $offset += 4;
         $chunk['type_raw'] = Helper::BigEndian2Int($chunk['type_text']);
         $chunk['data'] = substr($PNGfiledata, $offset, $chunk['data_length']);
         $offset += $chunk['data_length'];
         $chunk['crc'] = Helper::BigEndian2Int(substr($PNGfiledata, $offset, 4));
         $offset += 4;
         $chunk['flags']['ancilliary'] = (bool) ($chunk['type_raw'] & 0x20000000);
         $chunk['flags']['private'] = (bool) ($chunk['type_raw'] & 0x200000);
         $chunk['flags']['reserved'] = (bool) ($chunk['type_raw'] & 0x2000);
         $chunk['flags']['safe_to_copy'] = (bool) ($chunk['type_raw'] & 0x20);
         // shortcut
         $thisfile_png[$chunk['type_text']] = array();
         $thisfile_png_chunk_type_text =& $thisfile_png[$chunk['type_text']];
         switch ($chunk['type_text']) {
             case 'IHDR':
                 // Image Header
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['width'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 4));
                 $thisfile_png_chunk_type_text['height'] = Helper::BigEndian2Int(substr($chunk['data'], 4, 4));
                 $thisfile_png_chunk_type_text['raw']['bit_depth'] = Helper::BigEndian2Int(substr($chunk['data'], 8, 1));
                 $thisfile_png_chunk_type_text['raw']['color_type'] = Helper::BigEndian2Int(substr($chunk['data'], 9, 1));
                 $thisfile_png_chunk_type_text['raw']['compression_method'] = Helper::BigEndian2Int(substr($chunk['data'], 10, 1));
                 $thisfile_png_chunk_type_text['raw']['filter_method'] = Helper::BigEndian2Int(substr($chunk['data'], 11, 1));
                 $thisfile_png_chunk_type_text['raw']['interlace_method'] = Helper::BigEndian2Int(substr($chunk['data'], 12, 1));
                 $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['raw']['compression_method']);
                 $thisfile_png_chunk_type_text['color_type']['palette'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x1);
                 $thisfile_png_chunk_type_text['color_type']['true_color'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x2);
                 $thisfile_png_chunk_type_text['color_type']['alpha'] = (bool) ($thisfile_png_chunk_type_text['raw']['color_type'] & 0x4);
                 $info['video']['resolution_x'] = $thisfile_png_chunk_type_text['width'];
                 $info['video']['resolution_y'] = $thisfile_png_chunk_type_text['height'];
                 $info['video']['bits_per_sample'] = $this->IHDRcalculateBitsPerSample($thisfile_png_chunk_type_text['raw']['color_type'], $thisfile_png_chunk_type_text['raw']['bit_depth']);
                 break;
             case 'PLTE':
                 // Palette
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $paletteoffset = 0;
                 for ($i = 0; $i <= 255; ++$i) {
                     //$thisfile_png_chunk_type_text['red'][$i]   = GetId3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
                     //$thisfile_png_chunk_type_text['green'][$i] = GetId3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
                     //$thisfile_png_chunk_type_text['blue'][$i]  = GetId3_lib::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
                     $red = Helper::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
                     $green = Helper::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
                     $blue = Helper::BigEndian2Int(substr($chunk['data'], $paletteoffset++, 1));
                     $thisfile_png_chunk_type_text[$i] = $red << 16 | $green << 8 | $blue;
                 }
                 break;
             case 'tRNS':
                 // Transparency
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 switch ($thisfile_png['IHDR']['raw']['color_type']) {
                     case 0:
                         $thisfile_png_chunk_type_text['transparent_color_gray'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 2));
                         break;
                     case 2:
                         $thisfile_png_chunk_type_text['transparent_color_red'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 2));
                         $thisfile_png_chunk_type_text['transparent_color_green'] = Helper::BigEndian2Int(substr($chunk['data'], 2, 2));
                         $thisfile_png_chunk_type_text['transparent_color_blue'] = Helper::BigEndian2Int(substr($chunk['data'], 4, 2));
                         break;
                     case 3:
                         for ($i = 0; $i < strlen($chunk['data']); ++$i) {
                             $thisfile_png_chunk_type_text['palette_opacity'][$i] = Helper::BigEndian2Int(substr($chunk['data'], $i, 1));
                         }
                         break;
                     case 4:
                     case 6:
                         $info['error'][] = 'Invalid color_type in tRNS chunk: ' . $thisfile_png['IHDR']['raw']['color_type'];
                     default:
                         $info['warning'][] = 'Unhandled color_type in tRNS chunk: ' . $thisfile_png['IHDR']['raw']['color_type'];
                         break;
                 }
                 break;
             case 'gAMA':
                 // Image Gamma
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['gamma'] = Helper::BigEndian2Int($chunk['data']) / 100000;
                 break;
             case 'cHRM':
                 // Primary Chromaticities
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['white_x'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 4)) / 100000;
                 $thisfile_png_chunk_type_text['white_y'] = Helper::BigEndian2Int(substr($chunk['data'], 4, 4)) / 100000;
                 $thisfile_png_chunk_type_text['red_y'] = Helper::BigEndian2Int(substr($chunk['data'], 8, 4)) / 100000;
                 $thisfile_png_chunk_type_text['red_y'] = Helper::BigEndian2Int(substr($chunk['data'], 12, 4)) / 100000;
                 $thisfile_png_chunk_type_text['green_y'] = Helper::BigEndian2Int(substr($chunk['data'], 16, 4)) / 100000;
                 $thisfile_png_chunk_type_text['green_y'] = Helper::BigEndian2Int(substr($chunk['data'], 20, 4)) / 100000;
                 $thisfile_png_chunk_type_text['blue_y'] = Helper::BigEndian2Int(substr($chunk['data'], 24, 4)) / 100000;
                 $thisfile_png_chunk_type_text['blue_y'] = Helper::BigEndian2Int(substr($chunk['data'], 28, 4)) / 100000;
                 break;
             case 'sRGB':
                 // Standard RGB Color Space
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['reindering_intent'] = Helper::BigEndian2Int($chunk['data']);
                 $thisfile_png_chunk_type_text['reindering_intent_text'] = $this->PNGsRGBintentLookup($thisfile_png_chunk_type_text['reindering_intent']);
                 break;
             case 'iCCP':
                 // Embedded ICC Profile
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 list($profilename, $compressiondata) = explode("", $chunk['data'], 2);
                 $thisfile_png_chunk_type_text['profile_name'] = $profilename;
                 $thisfile_png_chunk_type_text['compression_method'] = Helper::BigEndian2Int(substr($compressiondata, 0, 1));
                 $thisfile_png_chunk_type_text['compression_profile'] = substr($compressiondata, 1);
                 $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
                 break;
             case 'tEXt':
                 // Textual Data
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 list($keyword, $text) = explode("", $chunk['data'], 2);
                 $thisfile_png_chunk_type_text['keyword'] = $keyword;
                 $thisfile_png_chunk_type_text['text'] = $text;
                 $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
                 break;
             case 'zTXt':
                 // Compressed Textual Data
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 list($keyword, $otherdata) = explode("", $chunk['data'], 2);
                 $thisfile_png_chunk_type_text['keyword'] = $keyword;
                 $thisfile_png_chunk_type_text['compression_method'] = Helper::BigEndian2Int(substr($otherdata, 0, 1));
                 $thisfile_png_chunk_type_text['compressed_text'] = substr($otherdata, 1);
                 $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
                 switch ($thisfile_png_chunk_type_text['compression_method']) {
                     case 0:
                         $thisfile_png_chunk_type_text['text'] = gzuncompress($thisfile_png_chunk_type_text['compressed_text']);
                         break;
                     default:
                         // unknown compression method
                         break;
                 }
                 if (isset($thisfile_png_chunk_type_text['text'])) {
                     $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
                 }
                 break;
             case 'iTXt':
                 // International Textual Data
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 list($keyword, $otherdata) = explode("", $chunk['data'], 2);
                 $thisfile_png_chunk_type_text['keyword'] = $keyword;
                 $thisfile_png_chunk_type_text['compression'] = (bool) Helper::BigEndian2Int(substr($otherdata, 0, 1));
                 $thisfile_png_chunk_type_text['compression_method'] = Helper::BigEndian2Int(substr($otherdata, 1, 1));
                 $thisfile_png_chunk_type_text['compression_method_text'] = $this->PNGcompressionMethodLookup($thisfile_png_chunk_type_text['compression_method']);
                 list($languagetag, $translatedkeyword, $text) = explode("", substr($otherdata, 2), 3);
                 $thisfile_png_chunk_type_text['language_tag'] = $languagetag;
                 $thisfile_png_chunk_type_text['translated_keyword'] = $translatedkeyword;
                 if ($thisfile_png_chunk_type_text['compression']) {
                     switch ($thisfile_png_chunk_type_text['compression_method']) {
                         case 0:
                             $thisfile_png_chunk_type_text['text'] = gzuncompress($text);
                             break;
                         default:
                             // unknown compression method
                             break;
                     }
                 } else {
                     $thisfile_png_chunk_type_text['text'] = $text;
                 }
                 if (isset($thisfile_png_chunk_type_text['text'])) {
                     $thisfile_png['comments'][$thisfile_png_chunk_type_text['keyword']][] = $thisfile_png_chunk_type_text['text'];
                 }
                 break;
             case 'bKGD':
                 // Background Color
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 switch ($thisfile_png['IHDR']['raw']['color_type']) {
                     case 0:
                     case 4:
                         $thisfile_png_chunk_type_text['background_gray'] = Helper::BigEndian2Int($chunk['data']);
                         break;
                     case 2:
                     case 6:
                         $thisfile_png_chunk_type_text['background_red'] = Helper::BigEndian2Int(substr($chunk['data'], 0 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
                         $thisfile_png_chunk_type_text['background_green'] = Helper::BigEndian2Int(substr($chunk['data'], 1 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
                         $thisfile_png_chunk_type_text['background_blue'] = Helper::BigEndian2Int(substr($chunk['data'], 2 * $thisfile_png['IHDR']['raw']['bit_depth'], $thisfile_png['IHDR']['raw']['bit_depth']));
                         break;
                     case 3:
                         $thisfile_png_chunk_type_text['background_index'] = Helper::BigEndian2Int($chunk['data']);
                         break;
                     default:
                         break;
                 }
                 break;
             case 'pHYs':
                 // Physical Pixel Dimensions
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['pixels_per_unit_x'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 4));
                 $thisfile_png_chunk_type_text['pixels_per_unit_y'] = Helper::BigEndian2Int(substr($chunk['data'], 4, 4));
                 $thisfile_png_chunk_type_text['unit_specifier'] = Helper::BigEndian2Int(substr($chunk['data'], 8, 1));
                 $thisfile_png_chunk_type_text['unit'] = $this->PNGpHYsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
                 break;
             case 'sBIT':
                 // Significant Bits
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 switch ($thisfile_png['IHDR']['raw']['color_type']) {
                     case 0:
                         $thisfile_png_chunk_type_text['significant_bits_gray'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 1));
                         break;
                     case 2:
                     case 3:
                         $thisfile_png_chunk_type_text['significant_bits_red'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 1));
                         $thisfile_png_chunk_type_text['significant_bits_green'] = Helper::BigEndian2Int(substr($chunk['data'], 1, 1));
                         $thisfile_png_chunk_type_text['significant_bits_blue'] = Helper::BigEndian2Int(substr($chunk['data'], 2, 1));
                         break;
                     case 4:
                         $thisfile_png_chunk_type_text['significant_bits_gray'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 1));
                         $thisfile_png_chunk_type_text['significant_bits_alpha'] = Helper::BigEndian2Int(substr($chunk['data'], 1, 1));
                         break;
                     case 6:
                         $thisfile_png_chunk_type_text['significant_bits_red'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 1));
                         $thisfile_png_chunk_type_text['significant_bits_green'] = Helper::BigEndian2Int(substr($chunk['data'], 1, 1));
                         $thisfile_png_chunk_type_text['significant_bits_blue'] = Helper::BigEndian2Int(substr($chunk['data'], 2, 1));
                         $thisfile_png_chunk_type_text['significant_bits_alpha'] = Helper::BigEndian2Int(substr($chunk['data'], 3, 1));
                         break;
                     default:
                         break;
                 }
                 break;
             case 'sPLT':
                 // Suggested Palette
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 list($palettename, $otherdata) = explode("", $chunk['data'], 2);
                 $thisfile_png_chunk_type_text['palette_name'] = $palettename;
                 $sPLToffset = 0;
                 $thisfile_png_chunk_type_text['sample_depth_bits'] = Helper::BigEndian2Int(substr($otherdata, $sPLToffset, 1));
                 $sPLToffset += 1;
                 $thisfile_png_chunk_type_text['sample_depth_bytes'] = $thisfile_png_chunk_type_text['sample_depth_bits'] / 8;
                 $paletteCounter = 0;
                 while ($sPLToffset < strlen($otherdata)) {
                     $thisfile_png_chunk_type_text['red'][$paletteCounter] = Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
                     $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
                     $thisfile_png_chunk_type_text['green'][$paletteCounter] = Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
                     $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
                     $thisfile_png_chunk_type_text['blue'][$paletteCounter] = Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
                     $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
                     $thisfile_png_chunk_type_text['alpha'][$paletteCounter] = Helper::BigEndian2Int(substr($otherdata, $sPLToffset, $thisfile_png_chunk_type_text['sample_depth_bytes']));
                     $sPLToffset += $thisfile_png_chunk_type_text['sample_depth_bytes'];
                     $thisfile_png_chunk_type_text['frequency'][$paletteCounter] = Helper::BigEndian2Int(substr($otherdata, $sPLToffset, 2));
                     $sPLToffset += 2;
                     ++$paletteCounter;
                 }
                 break;
             case 'hIST':
                 // Palette Histogram
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $hISTcounter = 0;
                 while ($hISTcounter < strlen($chunk['data'])) {
                     $thisfile_png_chunk_type_text[$hISTcounter] = Helper::BigEndian2Int(substr($chunk['data'], $hISTcounter / 2, 2));
                     $hISTcounter += 2;
                 }
                 break;
             case 'tIME':
                 // Image Last-Modification Time
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['year'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 2));
                 $thisfile_png_chunk_type_text['month'] = Helper::BigEndian2Int(substr($chunk['data'], 2, 1));
                 $thisfile_png_chunk_type_text['day'] = Helper::BigEndian2Int(substr($chunk['data'], 3, 1));
                 $thisfile_png_chunk_type_text['hour'] = Helper::BigEndian2Int(substr($chunk['data'], 4, 1));
                 $thisfile_png_chunk_type_text['minute'] = Helper::BigEndian2Int(substr($chunk['data'], 5, 1));
                 $thisfile_png_chunk_type_text['second'] = Helper::BigEndian2Int(substr($chunk['data'], 6, 1));
                 $thisfile_png_chunk_type_text['unix'] = gmmktime($thisfile_png_chunk_type_text['hour'], $thisfile_png_chunk_type_text['minute'], $thisfile_png_chunk_type_text['second'], $thisfile_png_chunk_type_text['month'], $thisfile_png_chunk_type_text['day'], $thisfile_png_chunk_type_text['year']);
                 break;
             case 'oFFs':
                 // Image Offset
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['position_x'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 4), false, true);
                 $thisfile_png_chunk_type_text['position_y'] = Helper::BigEndian2Int(substr($chunk['data'], 4, 4), false, true);
                 $thisfile_png_chunk_type_text['unit_specifier'] = Helper::BigEndian2Int(substr($chunk['data'], 8, 1));
                 $thisfile_png_chunk_type_text['unit'] = $this->PNGoFFsUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
                 break;
             case 'pCAL':
                 // Calibration Of Pixel Values
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 list($calibrationname, $otherdata) = explode("", $chunk['data'], 2);
                 $thisfile_png_chunk_type_text['calibration_name'] = $calibrationname;
                 $pCALoffset = 0;
                 $thisfile_png_chunk_type_text['original_zero'] = Helper::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
                 $pCALoffset += 4;
                 $thisfile_png_chunk_type_text['original_max'] = Helper::BigEndian2Int(substr($chunk['data'], $pCALoffset, 4), false, true);
                 $pCALoffset += 4;
                 $thisfile_png_chunk_type_text['equation_type'] = Helper::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
                 $pCALoffset += 1;
                 $thisfile_png_chunk_type_text['equation_type_text'] = $this->PNGpCALequationTypeLookup($thisfile_png_chunk_type_text['equation_type']);
                 $thisfile_png_chunk_type_text['parameter_count'] = Helper::BigEndian2Int(substr($chunk['data'], $pCALoffset, 1));
                 $pCALoffset += 1;
                 $thisfile_png_chunk_type_text['parameters'] = explode("", substr($chunk['data'], $pCALoffset));
                 break;
             case 'sCAL':
                 // Physical Scale Of Image Subject
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $thisfile_png_chunk_type_text['unit_specifier'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 1));
                 $thisfile_png_chunk_type_text['unit'] = $this->PNGsCALUnitLookup($thisfile_png_chunk_type_text['unit_specifier']);
                 list($pixelwidth, $pixelheight) = explode("", substr($chunk['data'], 1));
                 $thisfile_png_chunk_type_text['pixel_width'] = $pixelwidth;
                 $thisfile_png_chunk_type_text['pixel_height'] = $pixelheight;
                 break;
             case 'gIFg':
                 // GIF Graphic Control Extension
                 $gIFgCounter = 0;
                 if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
                     $gIFgCounter = count($thisfile_png_chunk_type_text);
                 }
                 $thisfile_png_chunk_type_text[$gIFgCounter]['header'] = $chunk;
                 $thisfile_png_chunk_type_text[$gIFgCounter]['disposal_method'] = Helper::BigEndian2Int(substr($chunk['data'], 0, 1));
                 $thisfile_png_chunk_type_text[$gIFgCounter]['user_input_flag'] = Helper::BigEndian2Int(substr($chunk['data'], 1, 1));
                 $thisfile_png_chunk_type_text[$gIFgCounter]['delay_time'] = Helper::BigEndian2Int(substr($chunk['data'], 2, 2));
                 break;
             case 'gIFx':
                 // GIF Application Extension
                 $gIFxCounter = 0;
                 if (isset($thisfile_png_chunk_type_text) && is_array($thisfile_png_chunk_type_text)) {
                     $gIFxCounter = count($thisfile_png_chunk_type_text);
                 }
                 $thisfile_png_chunk_type_text[$gIFxCounter]['header'] = $chunk;
                 $thisfile_png_chunk_type_text[$gIFxCounter]['application_identifier'] = substr($chunk['data'], 0, 8);
                 $thisfile_png_chunk_type_text[$gIFxCounter]['authentication_code'] = substr($chunk['data'], 8, 3);
                 $thisfile_png_chunk_type_text[$gIFxCounter]['application_data'] = substr($chunk['data'], 11);
                 break;
             case 'IDAT':
                 // Image Data
                 $idatinformationfieldindex = 0;
                 if (isset($thisfile_png['IDAT']) && is_array($thisfile_png['IDAT'])) {
                     $idatinformationfieldindex = count($thisfile_png['IDAT']);
                 }
                 unset($chunk['data']);
                 $thisfile_png_chunk_type_text[$idatinformationfieldindex]['header'] = $chunk;
                 break;
             case 'IEND':
                 // Image Trailer
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 break;
             default:
                 //unset($chunk['data']);
                 $thisfile_png_chunk_type_text['header'] = $chunk;
                 $info['warning'][] = 'Unhandled chunk type: ' . $chunk['type_text'];
                 break;
         }
     }
     return true;
 }
コード例 #24
0
ファイル: Vqf.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     // based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     $info['fileformat'] = 'vqf';
     $info['audio']['dataformat'] = 'vqf';
     $info['audio']['bitrate_mode'] = 'cbr';
     $info['audio']['lossless'] = false;
     // shortcut
     $info['vqf']['raw'] = array();
     $thisfile_vqf =& $info['vqf'];
     $thisfile_vqf_raw =& $thisfile_vqf['raw'];
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $VQFheaderData = fread($this->getid3->fp, 16);
     $offset = 0;
     $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
     $magic = 'TWIN';
     if ($thisfile_vqf_raw['header_tag'] != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($thisfile_vqf_raw['header_tag']) . '"';
         unset($info['vqf']);
         unset($info['fileformat']);
         return false;
     }
     $offset += 4;
     $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8);
     $offset += 8;
     $thisfile_vqf_raw['size'] = Helper::BigEndian2Int(substr($VQFheaderData, $offset, 4));
     $offset += 4;
     while (ftell($this->getid3->fp) < $info['avdataend']) {
         $ChunkBaseOffset = ftell($this->getid3->fp);
         $chunkoffset = 0;
         $ChunkData = fread($this->getid3->fp, 8);
         $ChunkName = substr($ChunkData, $chunkoffset, 4);
         if ($ChunkName == 'DATA') {
             $info['avdataoffset'] = $ChunkBaseOffset;
             break;
         }
         $chunkoffset += 4;
         $ChunkSize = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
         $chunkoffset += 4;
         if ($ChunkSize > $info['avdataend'] - ftell($this->getid3->fp)) {
             $info['error'][] = 'Invalid chunk size (' . $ChunkSize . ') for chunk "' . $ChunkName . '" at offset ' . $ChunkBaseOffset;
             break;
         }
         if ($ChunkSize > 0) {
             $ChunkData .= fread($this->getid3->fp, $ChunkSize);
         }
         switch ($ChunkName) {
             case 'COMM':
                 // shortcut
                 $thisfile_vqf['COMM'] = array();
                 $thisfile_vqf_COMM =& $thisfile_vqf['COMM'];
                 $thisfile_vqf_COMM['channel_mode'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['bitrate'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['sample_rate'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['security_level'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1;
                 $info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
                 $info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000;
                 $info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate'] / 1000);
                 if ($info['audio']['bitrate'] == 0) {
                     $info['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
                     return false;
                 }
                 break;
             case 'NAME':
             case 'AUTH':
             case '(c) ':
             case 'FILE':
             case 'COMT':
             case 'ALBM':
                 $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
                 break;
             case 'DSIZ':
                 $thisfile_vqf['DSIZ'] = Helper::BigEndian2Int(substr($ChunkData, 8, 4));
                 break;
             default:
                 $info['warning'][] = 'Unhandled chunk type "' . $ChunkName . '" at offset ' . $ChunkBaseOffset;
                 break;
         }
     }
     $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
     if (isset($thisfile_vqf['DSIZ']) && $thisfile_vqf['DSIZ'] != $info['avdataend'] - $info['avdataoffset'] - strlen('DATA')) {
         switch ($thisfile_vqf['DSIZ']) {
             case 0:
             case 1:
                 $info['warning'][] = 'Invalid DSIZ value "' . $thisfile_vqf['DSIZ'] . '". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v' . ($thisfile_vqf['DSIZ'] + 1) . '.0';
                 $info['audio']['encoder'] = 'Ahead Nero';
                 break;
             default:
                 $info['warning'][] = 'Probable corrupted file - should be ' . $thisfile_vqf['DSIZ'] . ' bytes, actually ' . ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'));
                 break;
         }
     }
     return true;
 }
コード例 #25
0
ファイル: Real.php プロジェクト: Nattpyre/rocketfiles
 /**
  * @param  type    $OldRAheaderData
  * @param  type    $ParsedArray
  *
  * @return bool
  *
  * @link http://www.freelists.org/archives/matroska-devel/07-2003/msg00010.html
  */
 public function ParseOldRAheader($OldRAheaderData, &$ParsedArray)
 {
     $ParsedArray = array();
     $ParsedArray['magic'] = substr($OldRAheaderData, 0, 4);
     if ($ParsedArray['magic'] != '.ra' . "ý") {
         return false;
     }
     $ParsedArray['version1'] = Helper::BigEndian2Int(substr($OldRAheaderData, 4, 2));
     if ($ParsedArray['version1'] < 3) {
         return false;
     } elseif ($ParsedArray['version1'] == 3) {
         $ParsedArray['fourcc1'] = '.ra3';
         $ParsedArray['bits_per_sample'] = 16;
         // hard-coded for old versions?
         $ParsedArray['sample_rate'] = 8000;
         // hard-coded for old versions?
         $ParsedArray['header_size'] = Helper::BigEndian2Int(substr($OldRAheaderData, 6, 2));
         $ParsedArray['channels'] = Helper::BigEndian2Int(substr($OldRAheaderData, 8, 2));
         // always 1 (?)
         //$ParsedArray['unknown1']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 10, 2));
         //$ParsedArray['unknown2']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 12, 2));
         //$ParsedArray['unknown3']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 14, 2));
         $ParsedArray['bytes_per_minute'] = Helper::BigEndian2Int(substr($OldRAheaderData, 16, 2));
         $ParsedArray['audio_bytes'] = Helper::BigEndian2Int(substr($OldRAheaderData, 18, 4));
         $ParsedArray['comments_raw'] = substr($OldRAheaderData, 22, $ParsedArray['header_size'] - 22 + 1);
         // not including null terminator
         $commentoffset = 0;
         $commentlength = Helper::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
         $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
         $commentoffset += $commentlength;
         $commentlength = Helper::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
         $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
         $commentoffset += $commentlength;
         $commentlength = Helper::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
         $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
         $commentoffset += $commentlength;
         ++$commentoffset;
         // final null terminator (?)
         ++$commentoffset;
         // fourcc length (?) should be 4
         $ParsedArray['fourcc'] = substr($OldRAheaderData, 23 + $commentoffset, 4);
     } elseif ($ParsedArray['version1'] <= 5) {
         //$ParsedArray['unknown1']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData,  6, 2));
         $ParsedArray['fourcc1'] = substr($OldRAheaderData, 8, 4);
         $ParsedArray['file_size'] = Helper::BigEndian2Int(substr($OldRAheaderData, 12, 4));
         $ParsedArray['version2'] = Helper::BigEndian2Int(substr($OldRAheaderData, 16, 2));
         $ParsedArray['header_size'] = Helper::BigEndian2Int(substr($OldRAheaderData, 18, 4));
         $ParsedArray['codec_flavor_id'] = Helper::BigEndian2Int(substr($OldRAheaderData, 22, 2));
         $ParsedArray['coded_frame_size'] = Helper::BigEndian2Int(substr($OldRAheaderData, 24, 4));
         $ParsedArray['audio_bytes'] = Helper::BigEndian2Int(substr($OldRAheaderData, 28, 4));
         $ParsedArray['bytes_per_minute'] = Helper::BigEndian2Int(substr($OldRAheaderData, 32, 4));
         //$ParsedArray['unknown5']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 36, 4));
         $ParsedArray['sub_packet_h'] = Helper::BigEndian2Int(substr($OldRAheaderData, 40, 2));
         $ParsedArray['frame_size'] = Helper::BigEndian2Int(substr($OldRAheaderData, 42, 2));
         $ParsedArray['sub_packet_size'] = Helper::BigEndian2Int(substr($OldRAheaderData, 44, 2));
         //$ParsedArray['unknown6']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 46, 2));
         switch ($ParsedArray['version1']) {
             case 4:
                 $ParsedArray['sample_rate'] = Helper::BigEndian2Int(substr($OldRAheaderData, 48, 2));
                 //$ParsedArray['unknown8']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 50, 2));
                 $ParsedArray['bits_per_sample'] = Helper::BigEndian2Int(substr($OldRAheaderData, 52, 2));
                 $ParsedArray['channels'] = Helper::BigEndian2Int(substr($OldRAheaderData, 54, 2));
                 $ParsedArray['length_fourcc2'] = Helper::BigEndian2Int(substr($OldRAheaderData, 56, 1));
                 $ParsedArray['fourcc2'] = substr($OldRAheaderData, 57, 4);
                 $ParsedArray['length_fourcc3'] = Helper::BigEndian2Int(substr($OldRAheaderData, 61, 1));
                 $ParsedArray['fourcc3'] = substr($OldRAheaderData, 62, 4);
                 //$ParsedArray['unknown9']         = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 66, 1));
                 //$ParsedArray['unknown10']        = GetId3_lib::BigEndian2Int(substr($OldRAheaderData, 67, 2));
                 $ParsedArray['comments_raw'] = substr($OldRAheaderData, 69, $ParsedArray['header_size'] - 69 + 16);
                 $commentoffset = 0;
                 $commentlength = Helper::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
                 $ParsedArray['comments']['title'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
                 $commentoffset += $commentlength;
                 $commentlength = Helper::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
                 $ParsedArray['comments']['artist'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
                 $commentoffset += $commentlength;
                 $commentlength = Helper::BigEndian2Int(substr($ParsedArray['comments_raw'], $commentoffset++, 1));
                 $ParsedArray['comments']['copyright'][] = substr($ParsedArray['comments_raw'], $commentoffset, $commentlength);
                 $commentoffset += $commentlength;
                 break;
             case 5:
                 $ParsedArray['sample_rate'] = Helper::BigEndian2Int(substr($OldRAheaderData, 48, 4));
                 $ParsedArray['sample_rate2'] = Helper::BigEndian2Int(substr($OldRAheaderData, 52, 4));
                 $ParsedArray['bits_per_sample'] = Helper::BigEndian2Int(substr($OldRAheaderData, 56, 4));
                 $ParsedArray['channels'] = Helper::BigEndian2Int(substr($OldRAheaderData, 60, 2));
                 $ParsedArray['genr'] = substr($OldRAheaderData, 62, 4);
                 $ParsedArray['fourcc3'] = substr($OldRAheaderData, 66, 4);
                 $ParsedArray['comments'] = array();
                 break;
         }
         $ParsedArray['fourcc'] = $ParsedArray['fourcc3'];
     }
     foreach ($ParsedArray['comments'] as $key => $value) {
         if ($ParsedArray['comments'][$key][0] === false) {
             $ParsedArray['comments'][$key][0] = '';
         }
     }
     return true;
 }