Пример #1
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $EXEheader = $this->fread(28);
     $magic = 'MZ';
     if (substr($EXEheader, 0, 2) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($EXEheader, 0, 2)) . '"';
         return false;
     }
     $info['fileformat'] = 'exe';
     $info['exe']['mz']['magic'] = 'MZ';
     $info['exe']['mz']['raw']['last_page_size'] = Utils::LittleEndian2Int(substr($EXEheader, 2, 2));
     $info['exe']['mz']['raw']['page_count'] = Utils::LittleEndian2Int(substr($EXEheader, 4, 2));
     $info['exe']['mz']['raw']['relocation_count'] = Utils::LittleEndian2Int(substr($EXEheader, 6, 2));
     $info['exe']['mz']['raw']['header_paragraphs'] = Utils::LittleEndian2Int(substr($EXEheader, 8, 2));
     $info['exe']['mz']['raw']['min_memory_paragraphs'] = Utils::LittleEndian2Int(substr($EXEheader, 10, 2));
     $info['exe']['mz']['raw']['max_memory_paragraphs'] = Utils::LittleEndian2Int(substr($EXEheader, 12, 2));
     $info['exe']['mz']['raw']['initial_ss'] = Utils::LittleEndian2Int(substr($EXEheader, 14, 2));
     $info['exe']['mz']['raw']['initial_sp'] = Utils::LittleEndian2Int(substr($EXEheader, 16, 2));
     $info['exe']['mz']['raw']['checksum'] = Utils::LittleEndian2Int(substr($EXEheader, 18, 2));
     $info['exe']['mz']['raw']['cs_ip'] = Utils::LittleEndian2Int(substr($EXEheader, 20, 4));
     $info['exe']['mz']['raw']['relocation_table_offset'] = Utils::LittleEndian2Int(substr($EXEheader, 24, 2));
     $info['exe']['mz']['raw']['overlay_number'] = Utils::LittleEndian2Int(substr($EXEheader, 26, 2));
     $info['exe']['mz']['byte_size'] = ($info['exe']['mz']['raw']['page_count'] - 1) * 512 + $info['exe']['mz']['raw']['last_page_size'];
     $info['exe']['mz']['header_size'] = $info['exe']['mz']['raw']['header_paragraphs'] * 16;
     $info['exe']['mz']['memory_minimum'] = $info['exe']['mz']['raw']['min_memory_paragraphs'] * 16;
     $info['exe']['mz']['memory_recommended'] = $info['exe']['mz']['raw']['max_memory_paragraphs'] * 16;
     $info['error'][] = 'EXE parsing not enabled in this version of getID3() [' . $this->getid3->version() . ']';
     return false;
 }
Пример #2
0
 public function ParseBink()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'bink';
     $info['video']['dataformat'] = 'bink';
     $fileData = 'BIK' . $this->fread(13);
     $info['bink']['data_size'] = Utils::LittleEndian2Int(substr($fileData, 4, 4));
     $info['bink']['frame_count'] = Utils::LittleEndian2Int(substr($fileData, 8, 2));
     if ($info['avdataend'] - $info['avdataoffset'] != $info['bink']['data_size'] + 8) {
         $info['error'][] = 'Probably truncated file: expecting ' . $info['bink']['data_size'] . ' bytes, found ' . ($info['avdataend'] - $info['avdataoffset']);
     }
     return true;
 }
Пример #3
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $RKAUHeader = $this->fread(20);
     $magic = 'RKA';
     if (substr($RKAUHeader, 0, 3) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($RKAUHeader, 0, 3)) . '"';
         return false;
     }
     $info['fileformat'] = 'rkau';
     $info['audio']['dataformat'] = 'rkau';
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['rkau']['raw']['version'] = Utils::LittleEndian2Int(substr($RKAUHeader, 3, 1));
     $info['rkau']['version'] = '1.' . str_pad($info['rkau']['raw']['version'] & 0xf, 2, '0', STR_PAD_LEFT);
     if ($info['rkau']['version'] > 1.07 || $info['rkau']['version'] < 1.06) {
         $info['error'][] = 'This version of getID3() [' . $this->getid3->version() . '] can only parse RKAU files v1.06 and 1.07 (this file is v' . $info['rkau']['version'] . ')';
         unset($info['rkau']);
         return false;
     }
     $info['rkau']['source_bytes'] = Utils::LittleEndian2Int(substr($RKAUHeader, 4, 4));
     $info['rkau']['sample_rate'] = Utils::LittleEndian2Int(substr($RKAUHeader, 8, 4));
     $info['rkau']['channels'] = Utils::LittleEndian2Int(substr($RKAUHeader, 12, 1));
     $info['rkau']['bits_per_sample'] = Utils::LittleEndian2Int(substr($RKAUHeader, 13, 1));
     $info['rkau']['raw']['quality'] = Utils::LittleEndian2Int(substr($RKAUHeader, 14, 1));
     $this->RKAUqualityLookup($info['rkau']);
     $info['rkau']['raw']['flags'] = Utils::LittleEndian2Int(substr($RKAUHeader, 15, 1));
     $info['rkau']['flags']['joint_stereo'] = (bool) (!($info['rkau']['raw']['flags'] & 0x1));
     $info['rkau']['flags']['streaming'] = (bool) ($info['rkau']['raw']['flags'] & 0x2);
     $info['rkau']['flags']['vrq_lossy_mode'] = (bool) ($info['rkau']['raw']['flags'] & 0x4);
     if ($info['rkau']['flags']['streaming']) {
         $info['avdataoffset'] += 20;
         $info['rkau']['compressed_bytes'] = Utils::LittleEndian2Int(substr($RKAUHeader, 16, 4));
     } else {
         $info['avdataoffset'] += 16;
         $info['rkau']['compressed_bytes'] = $info['avdataend'] - $info['avdataoffset'] - 1;
     }
     // Note: compressed_bytes does not always equal what appears to be the actual number of compressed bytes,
     // sometimes it's more, sometimes less. No idea why(?)
     $info['audio']['lossless'] = $info['rkau']['lossless'];
     $info['audio']['channels'] = $info['rkau']['channels'];
     $info['audio']['bits_per_sample'] = $info['rkau']['bits_per_sample'];
     $info['audio']['sample_rate'] = $info['rkau']['sample_rate'];
     $info['playtime_seconds'] = $info['rkau']['source_bytes'] / ($info['rkau']['sample_rate'] * $info['rkau']['channels'] * ($info['rkau']['bits_per_sample'] / 8));
     $info['audio']['bitrate'] = $info['rkau']['compressed_bytes'] * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #4
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $DSSheader = $this->fread(1540);
     if (!preg_match('#^(\\x02|\\x03)ds[s2]#', $DSSheader)) {
         $info['error'][] = 'Expecting "[02-03] 64 73 [73|32]" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($DSSheader, 0, 4)) . '"';
         return false;
     }
     // some structure information taken from http://cpansearch.perl.org/src/RGIBSON/Audio-DSS-0.02/lib/Audio/DSS.pm
     $info['encoding'] = 'ISO-8859-1';
     // not certain, but assumed
     $info['dss'] = array();
     $info['fileformat'] = 'dss';
     $info['mime_type'] = 'audio/x-' . substr($DSSheader, 1, 3);
     // "audio/x-dss" or "audio/x-ds2"
     $info['audio']['dataformat'] = substr($DSSheader, 1, 3);
     //         "dss" or         "ds2"
     $info['audio']['bitrate_mode'] = 'cbr';
     $info['dss']['version'] = ord(substr($DSSheader, 0, 1));
     $info['dss']['hardware'] = trim(substr($DSSheader, 12, 16));
     // identification string for hardware used to create the file, e.g. "DPM 9600", "DS2400"
     $info['dss']['unknown1'] = Utils::LittleEndian2Int(substr($DSSheader, 28, 4));
     // 32-37 = "FE FF FE FF F7 FF" in all the sample files I've seen
     $info['dss']['date_create'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
     $info['dss']['date_complete'] = $this->DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
     $info['dss']['playtime_sec'] = intval(substr($DSSheader, 62, 2) * 3600 + substr($DSSheader, 64, 2) * 60 + substr($DSSheader, 66, 2));
     // approximate file playtime in HHMMSS
     $info['dss']['playtime_ms'] = Utils::LittleEndian2Int(substr($DSSheader, 512, 4));
     // exact file playtime in milliseconds. Has also been observed at offset 530 in one sample file, with something else (unknown) at offset 512
     $info['dss']['priority'] = ord(substr($DSSheader, 793, 1));
     $info['dss']['comments'] = trim(substr($DSSheader, 798, 100));
     $info['dss']['sample_rate_index'] = ord(substr($DSSheader, 1538, 1));
     // this isn't certain, this may or may not be where the sample rate info is stored, but it seems consistent on my small selection of sample files
     $info['audio']['bits_per_sample'] = 16;
     // maybe, maybe not -- most compressed audio formats don't have a fixed bits-per-sample value, but this is a reasonable approximation
     $info['audio']['sample_rate'] = $this->DSSsampleRateLookup($info['dss']['sample_rate_index']);
     $info['audio']['channels'] = 1;
     $info['playtime_seconds'] = $info['dss']['playtime_ms'] / 1000;
     if (floor($info['dss']['playtime_ms'] / 1000) != $info['dss']['playtime_sec']) {
         // *should* just be playtime_ms / 1000 but at least one sample file has playtime_ms at offset 530 instead of offset 512, so safety check
         $info['playtime_seconds'] = $info['dss']['playtime_sec'];
         $this->getid3->warning('playtime_ms (' . number_format($info['dss']['playtime_ms'] / 1000, 3) . ') does not match playtime_sec (' . number_format($info['dss']['playtime_sec']) . ') - using playtime_sec value');
     }
     $info['audio']['bitrate'] = $info['filesize'] * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #5
0
 public function TIFFendian2Int($bytestring, $byteorder)
 {
     if ($byteorder == 'Intel') {
         return Utils::LittleEndian2Int($bytestring);
     } elseif ($byteorder == 'Motorola') {
         return Utils::BigEndian2Int($bytestring);
     }
     return false;
 }
Пример #6
0
 public function getNSVfHeaderFilepointer($fileoffset, $getTOCoffsets = false)
 {
     $info =& $this->getid3->info;
     $this->fseek($fileoffset);
     $NSVfheader = $this->fread(28);
     $offset = 0;
     $info['nsv']['NSVf']['identifier'] = substr($NSVfheader, $offset, 4);
     $offset += 4;
     if ($info['nsv']['NSVf']['identifier'] != 'NSVf') {
         $info['error'][] = 'expected "NSVf" at offset (' . $fileoffset . '), found "' . $info['nsv']['NSVf']['identifier'] . '" instead';
         unset($info['nsv']['NSVf']);
         return false;
     }
     $info['nsv']['NSVs']['offset'] = $fileoffset;
     $info['nsv']['NSVf']['header_length'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['file_size'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     if ($info['nsv']['NSVf']['file_size'] > $info['avdataend']) {
         $info['warning'][] = 'truncated file - NSVf header indicates ' . $info['nsv']['NSVf']['file_size'] . ' bytes, file actually ' . $info['avdataend'] . ' bytes';
     }
     $info['nsv']['NSVf']['playtime_ms'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['meta_size'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['TOC_entries_1'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     $info['nsv']['NSVf']['TOC_entries_2'] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
     $offset += 4;
     if ($info['nsv']['NSVf']['playtime_ms'] == 0) {
         $info['error'][] = 'Corrupt NSV file: NSVf.playtime_ms == zero';
         return false;
     }
     $NSVfheader .= $this->fread($info['nsv']['NSVf']['meta_size'] + 4 * $info['nsv']['NSVf']['TOC_entries_1'] + 4 * $info['nsv']['NSVf']['TOC_entries_2']);
     $NSVfheaderlength = strlen($NSVfheader);
     $info['nsv']['NSVf']['metadata'] = substr($NSVfheader, $offset, $info['nsv']['NSVf']['meta_size']);
     $offset += $info['nsv']['NSVf']['meta_size'];
     if ($getTOCoffsets) {
         $TOCcounter = 0;
         while ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
             if ($TOCcounter < $info['nsv']['NSVf']['TOC_entries_1']) {
                 $info['nsv']['NSVf']['TOC_1'][$TOCcounter] = Utils::LittleEndian2Int(substr($NSVfheader, $offset, 4));
                 $offset += 4;
                 $TOCcounter++;
             }
         }
     }
     if (trim($info['nsv']['NSVf']['metadata']) != '') {
         $info['nsv']['NSVf']['metadata'] = str_replace('`', "", $info['nsv']['NSVf']['metadata']);
         $CommentPairArray = explode("" . ' ', $info['nsv']['NSVf']['metadata']);
         foreach ($CommentPairArray as $CommentPair) {
             if (strstr($CommentPair, '=' . "")) {
                 list($key, $value) = explode('=' . "", $CommentPair, 2);
                 $info['nsv']['comments'][strtolower($key)][] = trim(str_replace("", '', $value));
             }
         }
     }
     $info['playtime_seconds'] = $info['nsv']['NSVf']['playtime_ms'] / 1000;
     $info['bitrate'] = $info['nsv']['NSVf']['file_size'] * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #7
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $OriginalAVdataOffset = $info['avdataoffset'];
     $this->fseek($info['avdataoffset']);
     $VOCheader = $this->fread(26);
     $magic = 'Creative Voice File';
     if (substr($VOCheader, 0, 19) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($VOCheader, 0, 19)) . '"';
         return false;
     }
     // shortcuts
     $thisfile_audio =& $info['audio'];
     $info['voc'] = array();
     $thisfile_voc =& $info['voc'];
     $info['fileformat'] = 'voc';
     $thisfile_audio['dataformat'] = 'voc';
     $thisfile_audio['bitrate_mode'] = 'cbr';
     $thisfile_audio['lossless'] = true;
     $thisfile_audio['channels'] = 1;
     // might be overriden below
     $thisfile_audio['bits_per_sample'] = 8;
     // might be overriden below
     // byte #     Description
     // ------     ------------------------------------------
     // 00-12      'Creative Voice File'
     // 13         1A (eof to abort printing of file)
     // 14-15      Offset of first datablock in .voc file (std 1A 00 in Intel Notation)
     // 16-17      Version number (minor,major) (VOC-HDR puts 0A 01)
     // 18-19      2's Comp of Ver. # + 1234h (VOC-HDR puts 29 11)
     $thisfile_voc['header']['datablock_offset'] = Utils::LittleEndian2Int(substr($VOCheader, 20, 2));
     $thisfile_voc['header']['minor_version'] = Utils::LittleEndian2Int(substr($VOCheader, 22, 1));
     $thisfile_voc['header']['major_version'] = Utils::LittleEndian2Int(substr($VOCheader, 23, 1));
     do {
         $BlockOffset = $this->ftell();
         $BlockData = $this->fread(4);
         $BlockType = ord($BlockData[0]);
         $BlockSize = Utils::LittleEndian2Int(substr($BlockData, 1, 3));
         $ThisBlock = array();
         Utils::safe_inc($thisfile_voc['blocktypes'][$BlockType], 1);
         switch ($BlockType) {
             case 0:
                 // Terminator
                 // do nothing, we'll break out of the loop down below
                 break;
             case 1:
                 // Sound data
                 $BlockData .= $this->fread(2);
                 if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
                     $info['avdataoffset'] = $this->ftell();
                 }
                 $this->fseek($BlockSize - 2, SEEK_CUR);
                 $ThisBlock['sample_rate_id'] = Utils::LittleEndian2Int(substr($BlockData, 4, 1));
                 $ThisBlock['compression_type'] = Utils::LittleEndian2Int(substr($BlockData, 5, 1));
                 $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
                 if ($ThisBlock['compression_type'] <= 3) {
                     $thisfile_voc['compressed_bits_per_sample'] = Utils::CastAsInt(str_replace('-bit', '', $ThisBlock['compression_name']));
                 }
                 // Less accurate sample_rate calculation than the Extended block (#8) data (but better than nothing if Extended Block is not available)
                 if (empty($thisfile_audio['sample_rate'])) {
                     // SR byte = 256 - (1000000 / sample_rate)
                     $thisfile_audio['sample_rate'] = Utils::trunc(1000000 / (256 - $ThisBlock['sample_rate_id']) / $thisfile_audio['channels']);
                 }
                 break;
             case 2:
                 // Sound continue
             // Sound continue
             case 3:
                 // Silence
             // Silence
             case 4:
                 // Marker
             // Marker
             case 6:
                 // Repeat
             // Repeat
             case 7:
                 // End repeat
                 // nothing useful, just skip
                 $this->fseek($BlockSize, SEEK_CUR);
                 break;
             case 8:
                 // Extended
                 $BlockData .= $this->fread(4);
                 //00-01  Time Constant:
                 //   Mono: 65536 - (256000000 / sample_rate)
                 // Stereo: 65536 - (256000000 / (sample_rate * 2))
                 $ThisBlock['time_constant'] = Utils::LittleEndian2Int(substr($BlockData, 4, 2));
                 $ThisBlock['pack_method'] = Utils::LittleEndian2Int(substr($BlockData, 6, 1));
                 $ThisBlock['stereo'] = (bool) Utils::LittleEndian2Int(substr($BlockData, 7, 1));
                 $thisfile_audio['channels'] = $ThisBlock['stereo'] ? 2 : 1;
                 $thisfile_audio['sample_rate'] = Utils::trunc(256000000 / (65536 - $ThisBlock['time_constant']) / $thisfile_audio['channels']);
                 break;
             case 9:
                 // data block that supersedes blocks 1 and 8. Used for stereo, 16 bit
                 $BlockData .= $this->fread(12);
                 if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
                     $info['avdataoffset'] = $this->ftell();
                 }
                 $this->fseek($BlockSize - 12, SEEK_CUR);
                 $ThisBlock['sample_rate'] = Utils::LittleEndian2Int(substr($BlockData, 4, 4));
                 $ThisBlock['bits_per_sample'] = Utils::LittleEndian2Int(substr($BlockData, 8, 1));
                 $ThisBlock['channels'] = Utils::LittleEndian2Int(substr($BlockData, 9, 1));
                 $ThisBlock['wFormat'] = Utils::LittleEndian2Int(substr($BlockData, 10, 2));
                 $ThisBlock['compression_name'] = $this->VOCwFormatLookup($ThisBlock['wFormat']);
                 if ($this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat'])) {
                     $thisfile_voc['compressed_bits_per_sample'] = $this->VOCwFormatActualBitsPerSampleLookup($ThisBlock['wFormat']);
                 }
                 $thisfile_audio['sample_rate'] = $ThisBlock['sample_rate'];
                 $thisfile_audio['bits_per_sample'] = $ThisBlock['bits_per_sample'];
                 $thisfile_audio['channels'] = $ThisBlock['channels'];
                 break;
             default:
                 $info['warning'][] = 'Unhandled block type "' . $BlockType . '" at offset ' . $BlockOffset;
                 $this->fseek($BlockSize, SEEK_CUR);
                 break;
         }
         if (!empty($ThisBlock)) {
             $ThisBlock['block_offset'] = $BlockOffset;
             $ThisBlock['block_size'] = $BlockSize;
             $ThisBlock['block_type_id'] = $BlockType;
             $thisfile_voc['blocks'][] = $ThisBlock;
         }
     } while (!feof($this->getid3->fp) && $BlockType != 0);
     // Terminator block doesn't have size field, so seek back 3 spaces
     $this->fseek(-3, SEEK_CUR);
     ksort($thisfile_voc['blocktypes']);
     if (!empty($thisfile_voc['compressed_bits_per_sample'])) {
         $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / ($thisfile_voc['compressed_bits_per_sample'] * $thisfile_audio['channels'] * $thisfile_audio['sample_rate']);
         $thisfile_audio['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     return true;
 }
Пример #8
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'tta';
     $info['audio']['dataformat'] = 'tta';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'vbr';
     $this->fseek($info['avdataoffset']);
     $ttaheader = $this->fread(26);
     $info['tta']['magic'] = substr($ttaheader, 0, 3);
     $magic = 'TTA';
     if ($info['tta']['magic'] != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($info['tta']['magic']) . '"';
         unset($info['fileformat']);
         unset($info['audio']);
         unset($info['tta']);
         return false;
     }
     switch ($ttaheader[3]) {
         case "":
             // TTA v1.x
         // TTA v1.x
         case "":
             // TTA v1.x
         // TTA v1.x
         case "":
             // TTA v1.x
             // "It was the demo-version of the TTA encoder. There is no released format with such header. TTA encoder v1 is not supported about a year."
             $info['tta']['major_version'] = 1;
             $info['avdataoffset'] += 16;
             $info['tta']['compression_level'] = ord($ttaheader[3]);
             $info['tta']['channels'] = Utils::LittleEndian2Int(substr($ttaheader, 4, 2));
             $info['tta']['bits_per_sample'] = Utils::LittleEndian2Int(substr($ttaheader, 6, 2));
             $info['tta']['sample_rate'] = Utils::LittleEndian2Int(substr($ttaheader, 8, 4));
             $info['tta']['samples_per_channel'] = Utils::LittleEndian2Int(substr($ttaheader, 12, 4));
             $info['audio']['encoder_options'] = '-e' . $info['tta']['compression_level'];
             $info['playtime_seconds'] = $info['tta']['samples_per_channel'] / $info['tta']['sample_rate'];
             break;
         case '2':
             // TTA v2.x
             // "I have hurried to release the TTA 2.0 encoder. Format documentation is removed from our site. This format still in development. Please wait the TTA2 format, encoder v4."
             $info['tta']['major_version'] = 2;
             $info['avdataoffset'] += 20;
             $info['tta']['compression_level'] = Utils::LittleEndian2Int(substr($ttaheader, 4, 2));
             $info['tta']['audio_format'] = Utils::LittleEndian2Int(substr($ttaheader, 6, 2));
             $info['tta']['channels'] = Utils::LittleEndian2Int(substr($ttaheader, 8, 2));
             $info['tta']['bits_per_sample'] = Utils::LittleEndian2Int(substr($ttaheader, 10, 2));
             $info['tta']['sample_rate'] = Utils::LittleEndian2Int(substr($ttaheader, 12, 4));
             $info['tta']['data_length'] = Utils::LittleEndian2Int(substr($ttaheader, 16, 4));
             $info['audio']['encoder_options'] = '-e' . $info['tta']['compression_level'];
             $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
             break;
         case '1':
             // TTA v3.x
             // "This is a first stable release of the TTA format. It will be supported by the encoders v3 or higher."
             $info['tta']['major_version'] = 3;
             $info['avdataoffset'] += 26;
             $info['tta']['audio_format'] = Utils::LittleEndian2Int(substr($ttaheader, 4, 2));
             $info['tta']['channels'] = Utils::LittleEndian2Int(substr($ttaheader, 6, 2));
             $info['tta']['bits_per_sample'] = Utils::LittleEndian2Int(substr($ttaheader, 8, 2));
             $info['tta']['sample_rate'] = Utils::LittleEndian2Int(substr($ttaheader, 10, 4));
             $info['tta']['data_length'] = Utils::LittleEndian2Int(substr($ttaheader, 14, 4));
             $info['tta']['crc32_footer'] = substr($ttaheader, 18, 4);
             $info['tta']['seek_point'] = Utils::LittleEndian2Int(substr($ttaheader, 22, 4));
             $info['playtime_seconds'] = $info['tta']['data_length'] / $info['tta']['sample_rate'];
             break;
         default:
             $info['error'][] = 'This version of getID3() [' . $this->getid3->version() . '] only knows how to handle TTA v1 and v2 - it may not work correctly with this file which appears to be TTA v' . $ttaheader[3];
             return false;
             break;
     }
     $info['audio']['encoder'] = 'TTA v' . $info['tta']['major_version'];
     $info['audio']['bits_per_sample'] = $info['tta']['bits_per_sample'];
     $info['audio']['sample_rate'] = $info['tta']['sample_rate'];
     $info['audio']['channels'] = $info['tta']['channels'];
     $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     return true;
 }
Пример #9
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     while (true) {
         $wavpackheader = $this->fread(32);
         if ($this->ftell() >= $info['avdataend']) {
             break;
         } elseif (feof($this->getid3->fp)) {
             break;
         } elseif (isset($info['wavpack']['blockheader']['total_samples']) && isset($info['wavpack']['blockheader']['block_samples']) && $info['wavpack']['blockheader']['total_samples'] > 0 && $info['wavpack']['blockheader']['block_samples'] > 0 && (!isset($info['wavpack']['riff_trailer_size']) || $info['wavpack']['riff_trailer_size'] <= 0) && (isset($info['wavpack']['config_flags']['md5_checksum']) && $info['wavpack']['config_flags']['md5_checksum'] === false || !empty($info['md5_data_source']))) {
             break;
         }
         $blockheader_offset = $this->ftell() - 32;
         $blockheader_magic = substr($wavpackheader, 0, 4);
         $blockheader_size = Utils::LittleEndian2Int(substr($wavpackheader, 4, 4));
         $magic = 'wvpk';
         if ($blockheader_magic != $magic) {
             $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $blockheader_offset . ', found "' . Utils::PrintHexBytes($blockheader_magic) . '"';
             switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
                 case 'wavpack':
                 case 'wvc':
                     break;
                 default:
                     unset($info['fileformat']);
                     unset($info['audio']);
                     unset($info['wavpack']);
                     break;
             }
             return false;
         }
         if (empty($info['wavpack']['blockheader']['block_samples']) || empty($info['wavpack']['blockheader']['total_samples']) || $info['wavpack']['blockheader']['block_samples'] <= 0 || $info['wavpack']['blockheader']['total_samples'] <= 0) {
             // Also, it is possible that the first block might not have
             // any samples (block_samples == 0) and in this case you should skip blocks
             // until you find one with samples because the other information (like
             // total_samples) are not guaranteed to be correct until (block_samples > 0)
             // Finally, I have defined a format for files in which the length is not known
             // (for example when raw files are created using pipes). In these cases
             // total_samples will be -1 and you must seek to the final block to determine
             // the total number of samples.
             $info['audio']['dataformat'] = 'wavpack';
             $info['fileformat'] = 'wavpack';
             $info['audio']['lossless'] = true;
             $info['audio']['bitrate_mode'] = 'vbr';
             $info['wavpack']['blockheader']['offset'] = $blockheader_offset;
             $info['wavpack']['blockheader']['magic'] = $blockheader_magic;
             $info['wavpack']['blockheader']['size'] = $blockheader_size;
             if ($info['wavpack']['blockheader']['size'] >= 0x100000) {
                 $info['error'][] = 'Expecting WavPack block size less than "0x100000", found "' . $info['wavpack']['blockheader']['size'] . '" at offset ' . $info['wavpack']['blockheader']['offset'];
                 switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
                     case 'wavpack':
                     case 'wvc':
                         break;
                     default:
                         unset($info['fileformat']);
                         unset($info['audio']);
                         unset($info['wavpack']);
                         break;
                 }
                 return false;
             }
             $info['wavpack']['blockheader']['minor_version'] = ord($wavpackheader[8]);
             $info['wavpack']['blockheader']['major_version'] = ord($wavpackheader[9]);
             if ($info['wavpack']['blockheader']['major_version'] != 4 || $info['wavpack']['blockheader']['minor_version'] < 4 && $info['wavpack']['blockheader']['minor_version'] > 16) {
                 $info['error'][] = 'Expecting WavPack version between "4.2" and "4.16", found version "' . $info['wavpack']['blockheader']['major_version'] . '.' . $info['wavpack']['blockheader']['minor_version'] . '" at offset ' . $info['wavpack']['blockheader']['offset'];
                 switch (isset($info['audio']['dataformat']) ? $info['audio']['dataformat'] : '') {
                     case 'wavpack':
                     case 'wvc':
                         break;
                     default:
                         unset($info['fileformat']);
                         unset($info['audio']);
                         unset($info['wavpack']);
                         break;
                 }
                 return false;
             }
             $info['wavpack']['blockheader']['track_number'] = ord($wavpackheader[10]);
             // unused
             $info['wavpack']['blockheader']['index_number'] = ord($wavpackheader[11]);
             // unused
             $info['wavpack']['blockheader']['total_samples'] = Utils::LittleEndian2Int(substr($wavpackheader, 12, 4));
             $info['wavpack']['blockheader']['block_index'] = Utils::LittleEndian2Int(substr($wavpackheader, 16, 4));
             $info['wavpack']['blockheader']['block_samples'] = Utils::LittleEndian2Int(substr($wavpackheader, 20, 4));
             $info['wavpack']['blockheader']['flags_raw'] = Utils::LittleEndian2Int(substr($wavpackheader, 24, 4));
             $info['wavpack']['blockheader']['crc'] = Utils::LittleEndian2Int(substr($wavpackheader, 28, 4));
             $info['wavpack']['blockheader']['flags']['bytes_per_sample'] = 1 + ($info['wavpack']['blockheader']['flags_raw'] & 0x3);
             $info['wavpack']['blockheader']['flags']['mono'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x4);
             $info['wavpack']['blockheader']['flags']['hybrid'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x8);
             $info['wavpack']['blockheader']['flags']['joint_stereo'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x10);
             $info['wavpack']['blockheader']['flags']['cross_decorrelation'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x20);
             $info['wavpack']['blockheader']['flags']['hybrid_noiseshape'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x40);
             $info['wavpack']['blockheader']['flags']['ieee_32bit_float'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x80);
             $info['wavpack']['blockheader']['flags']['int_32bit'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x100);
             $info['wavpack']['blockheader']['flags']['hybrid_bitrate_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x200);
             $info['wavpack']['blockheader']['flags']['hybrid_balance_noise'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x400);
             $info['wavpack']['blockheader']['flags']['multichannel_initial'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x800);
             $info['wavpack']['blockheader']['flags']['multichannel_final'] = (bool) ($info['wavpack']['blockheader']['flags_raw'] & 0x1000);
             $info['audio']['lossless'] = !$info['wavpack']['blockheader']['flags']['hybrid'];
         }
         while (!feof($this->getid3->fp) && $this->ftell() < $blockheader_offset + $blockheader_size + 8) {
             $metablock = array('offset' => $this->ftell());
             $metablockheader = $this->fread(2);
             if (feof($this->getid3->fp)) {
                 break;
             }
             $metablock['id'] = ord($metablockheader[0]);
             $metablock['function_id'] = $metablock['id'] & 0x3f;
             $metablock['function_name'] = $this->WavPackMetablockNameLookup($metablock['function_id']);
             // The 0x20 bit in the id of the meta subblocks (which is defined as
             // ID_OPTIONAL_DATA) is a permanent part of the id. The idea is that
             // if a decoder encounters an id that it does not know about, it uses
             // that "ID_OPTIONAL_DATA" flag to determine what to do. If it is set
             // then the decoder simply ignores the metadata, but if it is zero
             // then the decoder should quit because it means that an understanding
             // of the metadata is required to correctly decode the audio.
             $metablock['non_decoder'] = (bool) ($metablock['id'] & 0x20);
             $metablock['padded_data'] = (bool) ($metablock['id'] & 0x40);
             $metablock['large_block'] = (bool) ($metablock['id'] & 0x80);
             if ($metablock['large_block']) {
                 $metablockheader .= $this->fread(2);
             }
             $metablock['size'] = Utils::LittleEndian2Int(substr($metablockheader, 1)) * 2;
             // size is stored in words
             $metablock['data'] = null;
             if ($metablock['size'] > 0) {
                 switch ($metablock['function_id']) {
                     case 0x21:
                         // ID_RIFF_HEADER
                     // ID_RIFF_HEADER
                     case 0x22:
                         // ID_RIFF_TRAILER
                     // ID_RIFF_TRAILER
                     case 0x23:
                         // ID_REPLAY_GAIN
                     // ID_REPLAY_GAIN
                     case 0x24:
                         // ID_CUESHEET
                     // ID_CUESHEET
                     case 0x25:
                         // ID_CONFIG_BLOCK
                     // ID_CONFIG_BLOCK
                     case 0x26:
                         // ID_MD5_CHECKSUM
                         $metablock['data'] = $this->fread($metablock['size']);
                         if ($metablock['padded_data']) {
                             // padded to the nearest even byte
                             $metablock['size']--;
                             $metablock['data'] = substr($metablock['data'], 0, -1);
                         }
                         break;
                     case 0x0:
                         // ID_DUMMY
                     // ID_DUMMY
                     case 0x1:
                         // ID_ENCODER_INFO
                     // ID_ENCODER_INFO
                     case 0x2:
                         // ID_DECORR_TERMS
                     // ID_DECORR_TERMS
                     case 0x3:
                         // ID_DECORR_WEIGHTS
                     // ID_DECORR_WEIGHTS
                     case 0x4:
                         // ID_DECORR_SAMPLES
                     // ID_DECORR_SAMPLES
                     case 0x5:
                         // ID_ENTROPY_VARS
                     // ID_ENTROPY_VARS
                     case 0x6:
                         // ID_HYBRID_PROFILE
                     // ID_HYBRID_PROFILE
                     case 0x7:
                         // ID_SHAPING_WEIGHTS
                     // ID_SHAPING_WEIGHTS
                     case 0x8:
                         // ID_FLOAT_INFO
                     // ID_FLOAT_INFO
                     case 0x9:
                         // ID_INT32_INFO
                     // ID_INT32_INFO
                     case 0xa:
                         // ID_WV_BITSTREAM
                     // ID_WV_BITSTREAM
                     case 0xb:
                         // ID_WVC_BITSTREAM
                     // ID_WVC_BITSTREAM
                     case 0xc:
                         // ID_WVX_BITSTREAM
                     // ID_WVX_BITSTREAM
                     case 0xd:
                         // ID_CHANNEL_INFO
                         $this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
                         break;
                     default:
                         $info['warning'][] = 'Unexpected metablock type "0x' . str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT) . '" at offset ' . $metablock['offset'];
                         $this->fseek($metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size']);
                         break;
                 }
                 switch ($metablock['function_id']) {
                     case 0x21:
                         // ID_RIFF_HEADER
                         $original_wav_filesize = Utils::LittleEndian2Int(substr($metablock['data'], 4, 4));
                         $getid3_temp = new GetID3();
                         $getid3_temp->openfile($this->getid3->filename);
                         $getid3_riff = new Riff($getid3_temp);
                         $getid3_riff->ParseRIFFdata($metablock['data']);
                         $metablock['riff'] = $getid3_temp->info['riff'];
                         $info['audio']['sample_rate'] = $getid3_temp->info['riff']['raw']['fmt ']['nSamplesPerSec'];
                         unset($getid3_riff, $getid3_temp);
                         $metablock['riff']['original_filesize'] = $original_wav_filesize;
                         $info['wavpack']['riff_trailer_size'] = $original_wav_filesize - $metablock['riff']['WAVE']['data'][0]['size'] - $metablock['riff']['header_size'];
                         $info['playtime_seconds'] = $info['wavpack']['blockheader']['total_samples'] / $info['audio']['sample_rate'];
                         // Safe RIFF header in case there's a RIFF footer later
                         $metablockRIFFheader = $metablock['data'];
                         break;
                     case 0x22:
                         // ID_RIFF_TRAILER
                         $metablockRIFFfooter = $metablockRIFFheader . $metablock['data'];
                         $startoffset = $metablock['offset'] + ($metablock['large_block'] ? 4 : 2);
                         $getid3_temp = new GetID3();
                         $getid3_temp->openfile($this->getid3->filename);
                         $getid3_temp->info['avdataend'] = $info['avdataend'];
                         //$getid3_temp->info['fileformat'] = 'riff';
                         $getid3_riff = new Riff($getid3_temp);
                         $metablock['riff'] = $getid3_riff->ParseRIFF($startoffset, $startoffset + $metablock['size']);
                         if (!empty($metablock['riff']['INFO'])) {
                             Riff::parseComments($metablock['riff']['INFO'], $metablock['comments']);
                             $info['tags']['riff'] = $metablock['comments'];
                         }
                         unset($getid3_temp, $getid3_riff);
                         break;
                     case 0x23:
                         // ID_REPLAY_GAIN
                         $info['warning'][] = 'WavPack "Replay Gain" contents not yet handled by getID3() in metablock at offset ' . $metablock['offset'];
                         break;
                     case 0x24:
                         // ID_CUESHEET
                         $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by getID3() in metablock at offset ' . $metablock['offset'];
                         break;
                     case 0x25:
                         // ID_CONFIG_BLOCK
                         $metablock['flags_raw'] = Utils::LittleEndian2Int(substr($metablock['data'], 0, 3));
                         $metablock['flags']['adobe_mode'] = (bool) ($metablock['flags_raw'] & 0x1);
                         // "adobe" mode for 32-bit floats
                         $metablock['flags']['fast_flag'] = (bool) ($metablock['flags_raw'] & 0x2);
                         // fast mode
                         $metablock['flags']['very_fast_flag'] = (bool) ($metablock['flags_raw'] & 0x4);
                         // double fast
                         $metablock['flags']['high_flag'] = (bool) ($metablock['flags_raw'] & 0x8);
                         // high quality mode
                         $metablock['flags']['very_high_flag'] = (bool) ($metablock['flags_raw'] & 0x10);
                         // double high (not used yet)
                         $metablock['flags']['bitrate_kbps'] = (bool) ($metablock['flags_raw'] & 0x20);
                         // bitrate is kbps, not bits / sample
                         $metablock['flags']['auto_shaping'] = (bool) ($metablock['flags_raw'] & 0x40);
                         // automatic noise shaping
                         $metablock['flags']['shape_override'] = (bool) ($metablock['flags_raw'] & 0x80);
                         // shaping mode specified
                         $metablock['flags']['joint_override'] = (bool) ($metablock['flags_raw'] & 0x100);
                         // joint-stereo mode specified
                         $metablock['flags']['copy_time'] = (bool) ($metablock['flags_raw'] & 0x200);
                         // copy file-time from source
                         $metablock['flags']['create_exe'] = (bool) ($metablock['flags_raw'] & 0x400);
                         // create executable
                         $metablock['flags']['create_wvc'] = (bool) ($metablock['flags_raw'] & 0x800);
                         // create correction file
                         $metablock['flags']['optimize_wvc'] = (bool) ($metablock['flags_raw'] & 0x1000);
                         // maximize bybrid compression
                         $metablock['flags']['quality_mode'] = (bool) ($metablock['flags_raw'] & 0x2000);
                         // psychoacoustic quality mode
                         $metablock['flags']['raw_flag'] = (bool) ($metablock['flags_raw'] & 0x4000);
                         // raw mode (not implemented yet)
                         $metablock['flags']['calc_noise'] = (bool) ($metablock['flags_raw'] & 0x8000);
                         // calc noise in hybrid mode
                         $metablock['flags']['lossy_mode'] = (bool) ($metablock['flags_raw'] & 0x10000);
                         // obsolete (for information)
                         $metablock['flags']['extra_mode'] = (bool) ($metablock['flags_raw'] & 0x20000);
                         // extra processing mode
                         $metablock['flags']['skip_wvx'] = (bool) ($metablock['flags_raw'] & 0x40000);
                         // no wvx stream w/ floats & big ints
                         $metablock['flags']['md5_checksum'] = (bool) ($metablock['flags_raw'] & 0x80000);
                         // compute & store MD5 signature
                         $metablock['flags']['quiet_mode'] = (bool) ($metablock['flags_raw'] & 0x100000);
                         // don't report progress %
                         $info['wavpack']['config_flags'] = $metablock['flags'];
                         $info['audio']['encoder_options'] = '';
                         if ($info['wavpack']['blockheader']['flags']['hybrid']) {
                             $info['audio']['encoder_options'] .= ' -b???';
                         }
                         $info['audio']['encoder_options'] .= $metablock['flags']['adobe_mode'] ? ' -a' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['optimize_wvc'] ? ' -cc' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['create_exe'] ? ' -e' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['fast_flag'] ? ' -f' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['joint_override'] ? ' -j?' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['high_flag'] ? ' -h' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['md5_checksum'] ? ' -m' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['calc_noise'] ? ' -n' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['shape_override'] ? ' -s?' : '';
                         $info['audio']['encoder_options'] .= $metablock['flags']['extra_mode'] ? ' -x?' : '';
                         if (!empty($info['audio']['encoder_options'])) {
                             $info['audio']['encoder_options'] = trim($info['audio']['encoder_options']);
                         } elseif (isset($info['audio']['encoder_options'])) {
                             unset($info['audio']['encoder_options']);
                         }
                         break;
                     case 0x26:
                         // ID_MD5_CHECKSUM
                         if (strlen($metablock['data']) == 16) {
                             $info['md5_data_source'] = strtolower(Utils::PrintHexBytes($metablock['data'], true, false, false));
                         } else {
                             $info['warning'][] = 'Expecting 16 bytes of WavPack "MD5 Checksum" in metablock at offset ' . $metablock['offset'] . ', but found ' . strlen($metablock['data']) . ' bytes';
                         }
                         break;
                     case 0x0:
                         // ID_DUMMY
                     // ID_DUMMY
                     case 0x1:
                         // ID_ENCODER_INFO
                     // ID_ENCODER_INFO
                     case 0x2:
                         // ID_DECORR_TERMS
                     // ID_DECORR_TERMS
                     case 0x3:
                         // ID_DECORR_WEIGHTS
                     // ID_DECORR_WEIGHTS
                     case 0x4:
                         // ID_DECORR_SAMPLES
                     // ID_DECORR_SAMPLES
                     case 0x5:
                         // ID_ENTROPY_VARS
                     // ID_ENTROPY_VARS
                     case 0x6:
                         // ID_HYBRID_PROFILE
                     // ID_HYBRID_PROFILE
                     case 0x7:
                         // ID_SHAPING_WEIGHTS
                     // ID_SHAPING_WEIGHTS
                     case 0x8:
                         // ID_FLOAT_INFO
                     // ID_FLOAT_INFO
                     case 0x9:
                         // ID_INT32_INFO
                     // ID_INT32_INFO
                     case 0xa:
                         // ID_WV_BITSTREAM
                     // ID_WV_BITSTREAM
                     case 0xb:
                         // ID_WVC_BITSTREAM
                     // ID_WVC_BITSTREAM
                     case 0xc:
                         // ID_WVX_BITSTREAM
                     // ID_WVX_BITSTREAM
                     case 0xd:
                         // ID_CHANNEL_INFO
                         unset($metablock);
                         break;
                 }
             }
             if (!empty($metablock)) {
                 $info['wavpack']['metablocks'][] = $metablock;
             }
         }
     }
     $info['audio']['encoder'] = 'WavPack v' . $info['wavpack']['blockheader']['major_version'] . '.' . str_pad($info['wavpack']['blockheader']['minor_version'], 2, '0', STR_PAD_LEFT);
     $info['audio']['bits_per_sample'] = $info['wavpack']['blockheader']['flags']['bytes_per_sample'] * 8;
     $info['audio']['channels'] = $info['wavpack']['blockheader']['flags']['mono'] ? 1 : 2;
     if (!empty($info['playtime_seconds'])) {
         $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     } else {
         $info['audio']['dataformat'] = 'wvc';
     }
     return true;
 }
Пример #10
0
 public function HandleBonkTags($BonkTagName)
 {
     $info =& $this->getid3->info;
     switch ($BonkTagName) {
         case 'BONK':
             // shortcut
             $thisfile_bonk_BONK =& $info['bonk']['BONK'];
             $BonkData = "" . 'BONK' . $this->fread(17);
             $thisfile_bonk_BONK['version'] = Utils::LittleEndian2Int(substr($BonkData, 5, 1));
             $thisfile_bonk_BONK['number_samples'] = Utils::LittleEndian2Int(substr($BonkData, 6, 4));
             $thisfile_bonk_BONK['sample_rate'] = Utils::LittleEndian2Int(substr($BonkData, 10, 4));
             $thisfile_bonk_BONK['channels'] = Utils::LittleEndian2Int(substr($BonkData, 14, 1));
             $thisfile_bonk_BONK['lossless'] = (bool) Utils::LittleEndian2Int(substr($BonkData, 15, 1));
             $thisfile_bonk_BONK['joint_stereo'] = (bool) Utils::LittleEndian2Int(substr($BonkData, 16, 1));
             $thisfile_bonk_BONK['number_taps'] = Utils::LittleEndian2Int(substr($BonkData, 17, 2));
             $thisfile_bonk_BONK['downsampling_ratio'] = Utils::LittleEndian2Int(substr($BonkData, 19, 1));
             $thisfile_bonk_BONK['samples_per_packet'] = Utils::LittleEndian2Int(substr($BonkData, 20, 2));
             $info['avdataoffset'] = $thisfile_bonk_BONK['offset'] + 5 + 17;
             $info['avdataend'] = $thisfile_bonk_BONK['offset'] + $thisfile_bonk_BONK['size'];
             $info['fileformat'] = 'bonk';
             $info['audio']['dataformat'] = 'bonk';
             $info['audio']['bitrate_mode'] = 'vbr';
             // assumed
             $info['audio']['channels'] = $thisfile_bonk_BONK['channels'];
             $info['audio']['sample_rate'] = $thisfile_bonk_BONK['sample_rate'];
             $info['audio']['channelmode'] = $thisfile_bonk_BONK['joint_stereo'] ? 'joint stereo' : 'stereo';
             $info['audio']['lossless'] = $thisfile_bonk_BONK['lossless'];
             $info['audio']['codec'] = 'bonk';
             $info['playtime_seconds'] = $thisfile_bonk_BONK['number_samples'] / ($thisfile_bonk_BONK['sample_rate'] * $thisfile_bonk_BONK['channels']);
             if ($info['playtime_seconds'] > 0) {
                 $info['audio']['bitrate'] = ($info['bonk']['dataend'] - $info['bonk']['dataoffset']) * 8 / $info['playtime_seconds'];
             }
             break;
         case 'INFO':
             // shortcut
             $thisfile_bonk_INFO =& $info['bonk']['INFO'];
             $thisfile_bonk_INFO['version'] = Utils::LittleEndian2Int($this->fread(1));
             $thisfile_bonk_INFO['entries_count'] = 0;
             $NextInfoDataPair = $this->fread(5);
             if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
                 while (!feof($this->getid3->fp)) {
                     //$CurrentSeekInfo['offset']  = Utils::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
                     //$CurrentSeekInfo['nextbit'] = Utils::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
                     //$thisfile_bonk_INFO[] = $CurrentSeekInfo;
                     $NextInfoDataPair = $this->fread(5);
                     if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
                         $this->fseek(-5, SEEK_CUR);
                         break;
                     }
                     $thisfile_bonk_INFO['entries_count']++;
                 }
             }
             break;
         case 'META':
             $BonkData = "" . 'META' . $this->fread($info['bonk']['META']['size'] - 5);
             $info['bonk']['META']['version'] = Utils::LittleEndian2Int(substr($BonkData, 5, 1));
             $MetaTagEntries = floor((strlen($BonkData) - 8 - 6) / 8);
             // BonkData - xxxxmeta - ØMETA
             $offset = 6;
             for ($i = 0; $i < $MetaTagEntries; $i++) {
                 $MetaEntryTagName = substr($BonkData, $offset, 4);
                 $offset += 4;
                 $MetaEntryTagOffset = Utils::LittleEndian2Int(substr($BonkData, $offset, 4));
                 $offset += 4;
                 $info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
             }
             break;
         case ' ID3':
             $info['audio']['encoder'] = 'Extended BONK v0.9+';
             $getid3_temp = new GetID3();
             $getid3_temp->openfile($this->getid3->filename);
             $getid3_id3v2 = new ID3v2($getid3_temp);
             $getid3_id3v2->StartingOffset = $info['bonk'][' ID3']['offset'] + 2;
             $info['bonk'][' ID3']['valid'] = $getid3_id3v2->Analyze();
             if ($info['bonk'][' ID3']['valid']) {
                 $info['id3v2'] = $getid3_temp->info['id3v2'];
             }
             unset($getid3_temp, $getid3_id3v2);
             break;
         default:
             $info['warning'][] = 'Unexpected Bonk tag "' . $BonkTagName . '" at offset ' . $info['bonk'][$BonkTagName]['offset'];
             break;
     }
 }
Пример #11
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     // based loosely on code from TMonkey by Jurgen Faul <jfaulØgmx*de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     $info['fileformat'] = 'mac';
     $info['audio']['dataformat'] = 'mac';
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['audio']['lossless'] = true;
     $info['monkeys_audio']['raw'] = array();
     $thisfile_monkeysaudio =& $info['monkeys_audio'];
     $thisfile_monkeysaudio_raw =& $thisfile_monkeysaudio['raw'];
     $this->fseek($info['avdataoffset']);
     $MACheaderData = $this->fread(74);
     $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
     $magic = 'MAC ';
     if ($thisfile_monkeysaudio_raw['magic'] != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($thisfile_monkeysaudio_raw['magic']) . '"';
         unset($info['fileformat']);
         return false;
     }
     $thisfile_monkeysaudio_raw['nVersion'] = Utils::LittleEndian2Int(substr($MACheaderData, 4, 2));
     // appears to be uint32 in 3.98+
     if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
         $thisfile_monkeysaudio_raw['nCompressionLevel'] = Utils::LittleEndian2Int(substr($MACheaderData, 6, 2));
         $thisfile_monkeysaudio_raw['nFormatFlags'] = Utils::LittleEndian2Int(substr($MACheaderData, 8, 2));
         $thisfile_monkeysaudio_raw['nChannels'] = Utils::LittleEndian2Int(substr($MACheaderData, 10, 2));
         $thisfile_monkeysaudio_raw['nSampleRate'] = Utils::LittleEndian2Int(substr($MACheaderData, 12, 4));
         $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, 16, 4));
         $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, 20, 4));
         $thisfile_monkeysaudio_raw['nTotalFrames'] = Utils::LittleEndian2Int(substr($MACheaderData, 24, 4));
         $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = Utils::LittleEndian2Int(substr($MACheaderData, 28, 4));
         $thisfile_monkeysaudio_raw['nPeakLevel'] = Utils::LittleEndian2Int(substr($MACheaderData, 32, 4));
         $thisfile_monkeysaudio_raw['nSeekElements'] = Utils::LittleEndian2Int(substr($MACheaderData, 38, 2));
         $offset = 8;
     } else {
         $offset = 8;
         // APE_DESCRIPTOR
         $thisfile_monkeysaudio_raw['nDescriptorBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nHeaderBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nSeekTableBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
         $offset += 16;
         // APE_HEADER
         $thisfile_monkeysaudio_raw['nCompressionLevel'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nFormatFlags'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nTotalFrames'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nBitsPerSample'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nChannels'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nSampleRate'] = Utils::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
     }
     $thisfile_monkeysaudio['flags']['8-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x1);
     $thisfile_monkeysaudio['flags']['crc-32'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x2);
     $thisfile_monkeysaudio['flags']['peak_level'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x4);
     $thisfile_monkeysaudio['flags']['24-bit'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x8);
     $thisfile_monkeysaudio['flags']['seek_elements'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x10);
     $thisfile_monkeysaudio['flags']['no_wav_header'] = (bool) ($thisfile_monkeysaudio_raw['nFormatFlags'] & 0x20);
     $thisfile_monkeysaudio['version'] = $thisfile_monkeysaudio_raw['nVersion'] / 1000;
     $thisfile_monkeysaudio['compression'] = $this->MonkeyCompressionLevelNameLookup($thisfile_monkeysaudio_raw['nCompressionLevel']);
     if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
         $thisfile_monkeysaudio['samples_per_frame'] = $this->MonkeySamplesPerFrame($thisfile_monkeysaudio_raw['nVersion'], $thisfile_monkeysaudio_raw['nCompressionLevel']);
     }
     $thisfile_monkeysaudio['bits_per_sample'] = $thisfile_monkeysaudio['flags']['24-bit'] ? 24 : ($thisfile_monkeysaudio['flags']['8-bit'] ? 8 : 16);
     $thisfile_monkeysaudio['channels'] = $thisfile_monkeysaudio_raw['nChannels'];
     $info['audio']['channels'] = $thisfile_monkeysaudio['channels'];
     $thisfile_monkeysaudio['sample_rate'] = $thisfile_monkeysaudio_raw['nSampleRate'];
     if ($thisfile_monkeysaudio['sample_rate'] == 0) {
         $info['error'][] = 'Corrupt MAC file: frequency == zero';
         return false;
     }
     $info['audio']['sample_rate'] = $thisfile_monkeysaudio['sample_rate'];
     if ($thisfile_monkeysaudio['flags']['peak_level']) {
         $thisfile_monkeysaudio['peak_level'] = $thisfile_monkeysaudio_raw['nPeakLevel'];
         $thisfile_monkeysaudio['peak_ratio'] = $thisfile_monkeysaudio['peak_level'] / pow(2, $thisfile_monkeysaudio['bits_per_sample'] - 1);
     }
     if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
         $thisfile_monkeysaudio['samples'] = ($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio_raw['nBlocksPerFrame'] + $thisfile_monkeysaudio_raw['nFinalFrameBlocks'];
     } else {
         $thisfile_monkeysaudio['samples'] = ($thisfile_monkeysaudio_raw['nTotalFrames'] - 1) * $thisfile_monkeysaudio['samples_per_frame'] + $thisfile_monkeysaudio_raw['nFinalFrameSamples'];
     }
     $thisfile_monkeysaudio['playtime'] = $thisfile_monkeysaudio['samples'] / $thisfile_monkeysaudio['sample_rate'];
     if ($thisfile_monkeysaudio['playtime'] == 0) {
         $info['error'][] = 'Corrupt MAC file: playtime == zero';
         return false;
     }
     $info['playtime_seconds'] = $thisfile_monkeysaudio['playtime'];
     $thisfile_monkeysaudio['compressed_size'] = $info['avdataend'] - $info['avdataoffset'];
     $thisfile_monkeysaudio['uncompressed_size'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * ($thisfile_monkeysaudio['bits_per_sample'] / 8);
     if ($thisfile_monkeysaudio['uncompressed_size'] == 0) {
         $info['error'][] = 'Corrupt MAC file: uncompressed_size == zero';
         return false;
     }
     $thisfile_monkeysaudio['compression_ratio'] = $thisfile_monkeysaudio['compressed_size'] / ($thisfile_monkeysaudio['uncompressed_size'] + $thisfile_monkeysaudio_raw['nHeaderDataBytes']);
     $thisfile_monkeysaudio['bitrate'] = $thisfile_monkeysaudio['samples'] * $thisfile_monkeysaudio['channels'] * $thisfile_monkeysaudio['bits_per_sample'] / $thisfile_monkeysaudio['playtime'] * $thisfile_monkeysaudio['compression_ratio'];
     $info['audio']['bitrate'] = $thisfile_monkeysaudio['bitrate'];
     // add size of MAC header to avdataoffset
     if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nDescriptorBytes'];
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderBytes'];
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nSeekTableBytes'];
         $info['avdataoffset'] += $thisfile_monkeysaudio_raw['nHeaderDataBytes'];
         $info['avdataend'] -= $thisfile_monkeysaudio_raw['nTerminatingDataBytes'];
     } else {
         $info['avdataoffset'] += $offset;
     }
     if ($thisfile_monkeysaudio_raw['nVersion'] >= 3980) {
         if ($thisfile_monkeysaudio_raw['cFileMD5'] === str_repeat("", 16)) {
             //$info['warning'][] = 'cFileMD5 is null';
         } else {
             $info['md5_data_source'] = '';
             $md5 = $thisfile_monkeysaudio_raw['cFileMD5'];
             for ($i = 0; $i < strlen($md5); $i++) {
                 $info['md5_data_source'] .= str_pad(dechex(ord($md5[$i])), 2, '00', STR_PAD_LEFT);
             }
             if (!preg_match('/^[0-9a-f]{32}$/', $info['md5_data_source'])) {
                 unset($info['md5_data_source']);
             }
         }
     }
     $info['audio']['bits_per_sample'] = $thisfile_monkeysaudio['bits_per_sample'];
     $info['audio']['encoder'] = 'MAC v' . number_format($thisfile_monkeysaudio['version'], 2);
     $info['audio']['encoder_options'] = ucfirst($thisfile_monkeysaudio['compression']) . ' compression';
     return true;
 }
Пример #12
0
 public function ParseOptimFROGheader45()
 {
     // for fileformat of v4.50a and higher
     $info =& $this->getid3->info;
     $RIFFdata = '';
     $this->fseek($info['avdataoffset']);
     while (!feof($this->getid3->fp) && $this->ftell() < $info['avdataend']) {
         $BlockOffset = $this->ftell();
         $BlockData = $this->fread(8);
         $offset = 8;
         $BlockName = substr($BlockData, 0, 4);
         $BlockSize = Utils::LittleEndian2Int(substr($BlockData, 4, 4));
         if ($BlockName == 'OFRX') {
             $BlockName = 'OFR ';
         }
         if (!isset($info['ofr'][$BlockName])) {
             $info['ofr'][$BlockName] = array();
         }
         $thisfile_ofr_thisblock =& $info['ofr'][$BlockName];
         switch ($BlockName) {
             case 'OFR ':
                 // shortcut
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $info['audio']['encoder'] = 'OptimFROG 4.50 alpha';
                 switch ($BlockSize) {
                     case 12:
                     case 15:
                         // good
                         break;
                     default:
                         $info['warning'][] = '"' . $BlockName . '" contains more data than expected (expected 12 or 15 bytes, found ' . $BlockSize . ' bytes)';
                         break;
                 }
                 $BlockData .= $this->fread($BlockSize);
                 $thisfile_ofr_thisblock['total_samples'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 6));
                 $offset += 6;
                 $thisfile_ofr_thisblock['raw']['sample_type'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $thisfile_ofr_thisblock['sample_type'] = $this->OptimFROGsampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
                 $offset += 1;
                 $thisfile_ofr_thisblock['channel_config'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
                 $offset += 1;
                 $thisfile_ofr_thisblock['sample_rate'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 if ($BlockSize > 12) {
                     // OFR 4.504b or higher
                     $thisfile_ofr_thisblock['channels'] = $this->OptimFROGchannelConfigNumChannelsLookup($thisfile_ofr_thisblock['channel_config']);
                     $thisfile_ofr_thisblock['raw']['encoder_id'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 2));
                     $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
                     $offset += 2;
                     $thisfile_ofr_thisblock['raw']['compression'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                     $thisfile_ofr_thisblock['compression'] = $this->OptimFROGcompressionLookup($thisfile_ofr_thisblock['raw']['compression']);
                     $thisfile_ofr_thisblock['speedup'] = $this->OptimFROGspeedupLookup($thisfile_ofr_thisblock['raw']['compression']);
                     $offset += 1;
                     $info['audio']['encoder'] = 'OptimFROG ' . $thisfile_ofr_thisblock['encoder'];
                     $info['audio']['encoder_options'] = '--mode ' . $thisfile_ofr_thisblock['compression'];
                     if (($thisfile_ofr_thisblock['raw']['encoder_id'] & 0xf0) >> 4 == 7) {
                         // v4.507
                         if (strtolower(Utils::fileextension($info['filename'])) == 'ofs') {
                             // OptimFROG DualStream format is lossy, but as of v4.507 there is no way to tell the difference
                             // between lossless and lossy other than the file extension.
                             $info['audio']['dataformat'] = 'ofs';
                             $info['audio']['lossless'] = true;
                         }
                     }
                 }
                 $info['audio']['channels'] = $thisfile_ofr_thisblock['channels'];
                 $info['audio']['sample_rate'] = $thisfile_ofr_thisblock['sample_rate'];
                 $info['audio']['bits_per_sample'] = $this->OptimFROGbitsPerSampleTypeLookup($thisfile_ofr_thisblock['raw']['sample_type']);
                 break;
             case 'COMP':
                 // unlike other block types, there CAN be multiple COMP blocks
                 $COMPdata['offset'] = $BlockOffset;
                 $COMPdata['size'] = $BlockSize;
                 if ($info['avdataoffset'] == 0) {
                     $info['avdataoffset'] = $BlockOffset;
                 }
                 // Only interested in first 14 bytes (only first 12 needed for v4.50 alpha), not actual audio data
                 $BlockData .= $this->fread(14);
                 $this->fseek($BlockSize - 14, SEEK_CUR);
                 $COMPdata['crc_32'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['sample_count'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['raw']['sample_type'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
                 $offset += 1;
                 $COMPdata['raw']['channel_configuration'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
                 $offset += 1;
                 $COMPdata['raw']['algorithm_id'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 2));
                 //$COMPdata['algorithm']                    = OptimFROGalgorithmNameLookup($COMPdata['raw']['algorithm_id']);
                 $offset += 2;
                 if ($info['ofr']['OFR ']['size'] > 12) {
                     // OFR 4.504b or higher
                     $COMPdata['raw']['encoder_id'] = Utils::LittleEndian2Int(substr($BlockData, $offset, 2));
                     $COMPdata['encoder'] = $this->OptimFROGencoderNameLookup($COMPdata['raw']['encoder_id']);
                     $offset += 2;
                 }
                 if ($COMPdata['crc_32'] == 0x454e4f4e) {
                     // ASCII value of 'NONE' - placeholder value in v4.50a
                     $COMPdata['crc_32'] = false;
                 }
                 $thisfile_ofr_thisblock[] = $COMPdata;
                 break;
             case 'HEAD':
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $RIFFdata .= $this->fread($BlockSize);
                 break;
             case 'TAIL':
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 if ($BlockSize > 0) {
                     $RIFFdata .= $this->fread($BlockSize);
                 }
                 break;
             case 'RECV':
                 // block contains no useful meta data - simply note and skip
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $this->fseek($BlockSize, SEEK_CUR);
                 break;
             case 'APET':
                 // APEtag v2
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $info['warning'][] = 'APEtag processing inside OptimFROG not supported in this version (' . $this->getid3->version() . ') of getID3()';
                 $this->fseek($BlockSize, SEEK_CUR);
                 break;
             case 'MD5 ':
                 // APEtag v2
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 if ($BlockSize == 16) {
                     $thisfile_ofr_thisblock['md5_binary'] = $this->fread($BlockSize);
                     $thisfile_ofr_thisblock['md5_string'] = Utils::PrintHexBytes($thisfile_ofr_thisblock['md5_binary'], true, false, false);
                     $info['md5_data_source'] = $thisfile_ofr_thisblock['md5_string'];
                 } else {
                     $info['warning'][] = 'Expecting block size of 16 in "MD5 " chunk, found ' . $BlockSize . ' instead';
                     $this->fseek($BlockSize, SEEK_CUR);
                 }
                 break;
             default:
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 $info['warning'][] = 'Unhandled OptimFROG block type "' . $BlockName . '" at offset ' . $thisfile_ofr_thisblock['offset'];
                 $this->fseek($BlockSize, SEEK_CUR);
                 break;
         }
     }
     if (isset($info['ofr']['TAIL']['offset'])) {
         $info['avdataend'] = $info['ofr']['TAIL']['offset'];
     }
     $info['playtime_seconds'] = (double) $info['ofr']['OFR ']['total_samples'] / ($info['audio']['channels'] * $info['audio']['sample_rate']);
     $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     // move the data chunk after all other chunks (if any)
     // so that the RIFF parser doesn't see EOF when trying
     // to skip over the data chunk
     $RIFFdata = substr($RIFFdata, 0, 36) . substr($RIFFdata, 44) . substr($RIFFdata, 36, 8);
     $getid3_temp = new GetID3();
     $getid3_temp->openfile($this->getid3->filename);
     $getid3_temp->info['avdataoffset'] = $info['avdataoffset'];
     $getid3_temp->info['avdataend'] = $info['avdataend'];
     $getid3_riff = new Riff($getid3_temp);
     $getid3_riff->ParseRIFFdata($RIFFdata);
     $info['riff'] = $getid3_temp->info['riff'];
     unset($getid3_riff, $getid3_temp, $RIFFdata);
     return true;
 }
Пример #13
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'gif';
     $info['video']['dataformat'] = 'gif';
     $info['video']['lossless'] = true;
     $info['video']['pixel_aspect_ratio'] = (double) 1;
     $this->fseek($info['avdataoffset']);
     $GIFheader = $this->fread(13);
     $offset = 0;
     $info['gif']['header']['raw']['identifier'] = substr($GIFheader, $offset, 3);
     $offset += 3;
     $magic = 'GIF';
     if ($info['gif']['header']['raw']['identifier'] != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($info['gif']['header']['raw']['identifier']) . '"';
         unset($info['fileformat']);
         unset($info['gif']);
         return false;
     }
     $info['gif']['header']['raw']['version'] = substr($GIFheader, $offset, 3);
     $offset += 3;
     $info['gif']['header']['raw']['width'] = Utils::LittleEndian2Int(substr($GIFheader, $offset, 2));
     $offset += 2;
     $info['gif']['header']['raw']['height'] = Utils::LittleEndian2Int(substr($GIFheader, $offset, 2));
     $offset += 2;
     $info['gif']['header']['raw']['flags'] = Utils::LittleEndian2Int(substr($GIFheader, $offset, 1));
     $offset += 1;
     $info['gif']['header']['raw']['bg_color_index'] = Utils::LittleEndian2Int(substr($GIFheader, $offset, 1));
     $offset += 1;
     $info['gif']['header']['raw']['aspect_ratio'] = Utils::LittleEndian2Int(substr($GIFheader, $offset, 1));
     $offset += 1;
     $info['video']['resolution_x'] = $info['gif']['header']['raw']['width'];
     $info['video']['resolution_y'] = $info['gif']['header']['raw']['height'];
     $info['gif']['version'] = $info['gif']['header']['raw']['version'];
     $info['gif']['header']['flags']['global_color_table'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x80);
     if ($info['gif']['header']['raw']['flags'] & 0x80) {
         // Number of bits per primary color available to the original image, minus 1
         $info['gif']['header']['bits_per_pixel'] = 3 * ((($info['gif']['header']['raw']['flags'] & 0x70) >> 4) + 1);
     } else {
         $info['gif']['header']['bits_per_pixel'] = 0;
     }
     $info['gif']['header']['flags']['global_color_sorted'] = (bool) ($info['gif']['header']['raw']['flags'] & 0x40);
     if ($info['gif']['header']['flags']['global_color_table']) {
         // the number of bytes contained in the Global Color Table. To determine that
         // actual size of the color table, raise 2 to [the value of the field + 1]
         $info['gif']['header']['global_color_size'] = pow(2, ($info['gif']['header']['raw']['flags'] & 0x7) + 1);
         $info['video']['bits_per_sample'] = ($info['gif']['header']['raw']['flags'] & 0x7) + 1;
     } else {
         $info['gif']['header']['global_color_size'] = 0;
     }
     if ($info['gif']['header']['raw']['aspect_ratio'] != 0) {
         // Aspect Ratio = (Pixel Aspect Ratio + 15) / 64
         $info['gif']['header']['aspect_ratio'] = ($info['gif']['header']['raw']['aspect_ratio'] + 15) / 64;
     }
     //		if ($info['gif']['header']['flags']['global_color_table']) {
     //			$GIFcolorTable = $this->fread(3 * $info['gif']['header']['global_color_size']);
     //			$offset = 0;
     //			for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) {
     //				$red   = Utils::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
     //				$green = Utils::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
     //				$blue  = Utils::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
     //				$info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
     //			}
     //		}
     //
     //		// Image Descriptor
     //		while (!feof($this->getid3->fp)) {
     //			$NextBlockTest = $this->fread(1);
     //			switch ($NextBlockTest) {
     //
     //				case ',': // ',' - Image separator character
     //
     //					$ImageDescriptorData = $NextBlockTest.$this->fread(9);
     //					$ImageDescriptor = array();
     //					$ImageDescriptor['image_left']   = Utils::LittleEndian2Int(substr($ImageDescriptorData, 1, 2));
     //					$ImageDescriptor['image_top']    = Utils::LittleEndian2Int(substr($ImageDescriptorData, 3, 2));
     //					$ImageDescriptor['image_width']  = Utils::LittleEndian2Int(substr($ImageDescriptorData, 5, 2));
     //					$ImageDescriptor['image_height'] = Utils::LittleEndian2Int(substr($ImageDescriptorData, 7, 2));
     //					$ImageDescriptor['flags_raw']    = Utils::LittleEndian2Int(substr($ImageDescriptorData, 9, 1));
     //					$ImageDescriptor['flags']['use_local_color_map'] = (bool) ($ImageDescriptor['flags_raw'] & 0x80);
     //					$ImageDescriptor['flags']['image_interlaced']    = (bool) ($ImageDescriptor['flags_raw'] & 0x40);
     //					$info['gif']['image_descriptor'][] = $ImageDescriptor;
     //
     //					if ($ImageDescriptor['flags']['use_local_color_map']) {
     //
     //						$info['warning'][] = 'This version of getID3() cannot parse local color maps for GIFs';
     //						return true;
     //
     //					}
     //echo 'Start of raster data: '.$this->ftell().'<BR>';
     //					$RasterData = array();
     //					$RasterData['code_size']        = Utils::LittleEndian2Int($this->fread(1));
     //					$RasterData['block_byte_count'] = Utils::LittleEndian2Int($this->fread(1));
     //					$info['gif']['raster_data'][count($info['gif']['image_descriptor']) - 1] = $RasterData;
     //
     //					$CurrentCodeSize = $RasterData['code_size'] + 1;
     //					for ($i = 0; $i < pow(2, $RasterData['code_size']); $i++) {
     //						$DefaultDataLookupTable[$i] = chr($i);
     //					}
     //					$DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 0] = ''; // Clear Code
     //					$DefaultDataLookupTable[pow(2, $RasterData['code_size']) + 1] = ''; // End Of Image Code
     //
     //
     //					$NextValue = $this->GetLSBits($CurrentCodeSize);
     //					echo 'Clear Code: '.$NextValue.'<BR>';
     //
     //					$NextValue = $this->GetLSBits($CurrentCodeSize);
     //					echo 'First Color: '.$NextValue.'<BR>';
     //
     //					$Prefix = $NextValue;
     //$i = 0;
     //					while ($i++ < 20) {
     //						$NextValue = $this->GetLSBits($CurrentCodeSize);
     //						echo $NextValue.'<BR>';
     //					}
     //return true;
     //					break;
     //
     //				case '!':
     //					// GIF Extension Block
     //					$ExtensionBlockData = $NextBlockTest.$this->fread(2);
     //					$ExtensionBlock = array();
     //					$ExtensionBlock['function_code']  = Utils::LittleEndian2Int(substr($ExtensionBlockData, 1, 1));
     //					$ExtensionBlock['byte_length']    = Utils::LittleEndian2Int(substr($ExtensionBlockData, 2, 1));
     //					$ExtensionBlock['data']           = $this->fread($ExtensionBlock['byte_length']);
     //					$info['gif']['extension_blocks'][] = $ExtensionBlock;
     //					break;
     //
     //				case ';':
     //					$info['gif']['terminator_offset'] = $this->ftell() - 1;
     //					// GIF Terminator
     //					break;
     //
     //				default:
     //					break;
     //
     //
     //			}
     //		}
     return true;
 }
Пример #14
0
 public function parseAPEheaderFooter($APEheaderFooterData)
 {
     // http://www.uni-jena.de/~pfk/mpp/sv8/apeheader.html
     // shortcut
     $headerfooterinfo['raw'] = array();
     $headerfooterinfo_raw =& $headerfooterinfo['raw'];
     $headerfooterinfo_raw['footer_tag'] = substr($APEheaderFooterData, 0, 8);
     if ($headerfooterinfo_raw['footer_tag'] != 'APETAGEX') {
         return false;
     }
     $headerfooterinfo_raw['version'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 8, 4));
     $headerfooterinfo_raw['tagsize'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 12, 4));
     $headerfooterinfo_raw['tag_items'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 16, 4));
     $headerfooterinfo_raw['global_flags'] = Utils::LittleEndian2Int(substr($APEheaderFooterData, 20, 4));
     $headerfooterinfo_raw['reserved'] = substr($APEheaderFooterData, 24, 8);
     $headerfooterinfo['tag_version'] = $headerfooterinfo_raw['version'] / 1000;
     if ($headerfooterinfo['tag_version'] >= 2) {
         $headerfooterinfo['flags'] = $this->parseAPEtagFlags($headerfooterinfo_raw['global_flags']);
     }
     return $headerfooterinfo;
 }
Пример #15
0
 public function ParseMPCsv6()
 {
     // this is SV4 - SV6
     $info =& $this->getid3->info;
     $thisfile_mpc_header =& $info['mpc']['header'];
     $offset = 0;
     $thisfile_mpc_header['size'] = 8;
     $this->fseek($info['avdataoffset']);
     $MPCheaderData = $this->fread($thisfile_mpc_header['size']);
     // add size of file header to avdataoffset - calc bitrate correctly + MD5 data
     $info['avdataoffset'] += $thisfile_mpc_header['size'];
     // Most of this code adapted from Jurgen Faul's MPEGplus source code - thanks Jurgen! :)
     $HeaderDWORD[0] = Utils::LittleEndian2Int(substr($MPCheaderData, 0, 4));
     $HeaderDWORD[1] = Utils::LittleEndian2Int(substr($MPCheaderData, 4, 4));
     // DDDD DDDD  CCCC CCCC  BBBB BBBB  AAAA AAAA
     // aaaa aaaa  abcd dddd  dddd deee  eeff ffff
     //
     // a = bitrate       = anything
     // b = IS            = anything
     // c = MS            = anything
     // d = streamversion = 0000000004 or 0000000005 or 0000000006
     // e = maxband       = anything
     // f = blocksize     = 000001 for SV5+, anything(?) for SV4
     $thisfile_mpc_header['target_bitrate'] = ($HeaderDWORD[0] & 0xff800000) >> 23;
     $thisfile_mpc_header['intensity_stereo'] = (bool) (($HeaderDWORD[0] & 0x400000) >> 22);
     $thisfile_mpc_header['mid_side_stereo'] = (bool) (($HeaderDWORD[0] & 0x200000) >> 21);
     $thisfile_mpc_header['stream_version_major'] = ($HeaderDWORD[0] & 0x1ff800) >> 11;
     $thisfile_mpc_header['stream_version_minor'] = 0;
     // no sub-version numbers before SV7
     $thisfile_mpc_header['max_band'] = ($HeaderDWORD[0] & 0x7c0) >> 6;
     // related to lowpass frequency, not sure how it translates exactly
     $thisfile_mpc_header['block_size'] = $HeaderDWORD[0] & 0x3f;
     switch ($thisfile_mpc_header['stream_version_major']) {
         case 4:
             $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1] >> 16;
             break;
         case 5:
         case 6:
             $thisfile_mpc_header['frame_count'] = $HeaderDWORD[1];
             break;
         default:
             $info['error'] = 'Expecting 4, 5 or 6 in version field, found ' . $thisfile_mpc_header['stream_version_major'] . ' instead';
             unset($info['mpc']);
             return false;
             break;
     }
     if ($thisfile_mpc_header['stream_version_major'] > 4 && $thisfile_mpc_header['block_size'] != 1) {
         $info['warning'][] = 'Block size expected to be 1, actual value found: ' . $thisfile_mpc_header['block_size'];
     }
     $thisfile_mpc_header['sample_rate'] = 44100;
     // AB: used by all files up to SV7
     $info['audio']['sample_rate'] = $thisfile_mpc_header['sample_rate'];
     $thisfile_mpc_header['samples'] = $thisfile_mpc_header['frame_count'] * 1152 * $info['audio']['channels'];
     if ($thisfile_mpc_header['target_bitrate'] == 0) {
         $info['audio']['bitrate_mode'] = 'vbr';
     } else {
         $info['audio']['bitrate_mode'] = 'cbr';
     }
     $info['mpc']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 * 44100 / $thisfile_mpc_header['frame_count'] / 1152;
     $info['audio']['bitrate'] = $info['mpc']['bitrate'];
     $info['audio']['encoder'] = 'SV' . $thisfile_mpc_header['stream_version_major'];
     return true;
 }
Пример #16
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $offset = 0;
     $this->fseek($info['avdataoffset']);
     $rawdata = $this->fread($this->getid3->fread_buffer_size());
     switch (substr($rawdata, $offset, 4)) {
         case 'LA02':
         case 'LA03':
         case 'LA04':
             $info['fileformat'] = 'la';
             $info['audio']['dataformat'] = 'la';
             $info['audio']['lossless'] = true;
             $info['la']['version_major'] = (int) substr($rawdata, $offset + 2, 1);
             $info['la']['version_minor'] = (int) substr($rawdata, $offset + 3, 1);
             $info['la']['version'] = (double) $info['la']['version_major'] + $info['la']['version_minor'] / 10;
             $offset += 4;
             $info['la']['uncompressed_size'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
             $offset += 4;
             if ($info['la']['uncompressed_size'] == 0) {
                 $info['error'][] = 'Corrupt LA file: uncompressed_size == zero';
                 return false;
             }
             $WAVEchunk = substr($rawdata, $offset, 4);
             if ($WAVEchunk !== 'WAVE') {
                 $info['error'][] = 'Expected "WAVE" (' . Utils::PrintHexBytes('WAVE') . ') at offset ' . $offset . ', found "' . $WAVEchunk . '" (' . Utils::PrintHexBytes($WAVEchunk) . ') instead.';
                 return false;
             }
             $offset += 4;
             $info['la']['fmt_size'] = 24;
             if ($info['la']['version'] >= 0.3) {
                 $info['la']['fmt_size'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
                 $info['la']['header_size'] = 49 + $info['la']['fmt_size'] - 24;
                 $offset += 4;
             } else {
                 // version 0.2 didn't support additional data blocks
                 $info['la']['header_size'] = 41;
             }
             $fmt_chunk = substr($rawdata, $offset, 4);
             if ($fmt_chunk !== 'fmt ') {
                 $info['error'][] = 'Expected "fmt " (' . Utils::PrintHexBytes('fmt ') . ') at offset ' . $offset . ', found "' . $fmt_chunk . '" (' . Utils::PrintHexBytes($fmt_chunk) . ') instead.';
                 return false;
             }
             $offset += 4;
             $fmt_size = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
             $offset += 4;
             $info['la']['raw']['format'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 2));
             $offset += 2;
             $info['la']['channels'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 2));
             $offset += 2;
             if ($info['la']['channels'] == 0) {
                 $info['error'][] = 'Corrupt LA file: channels == zero';
                 return false;
             }
             $info['la']['sample_rate'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
             $offset += 4;
             if ($info['la']['sample_rate'] == 0) {
                 $info['error'][] = 'Corrupt LA file: sample_rate == zero';
                 return false;
             }
             $info['la']['bytes_per_second'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
             $offset += 4;
             $info['la']['bytes_per_sample'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 2));
             $offset += 2;
             $info['la']['bits_per_sample'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 2));
             $offset += 2;
             $info['la']['samples'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
             $offset += 4;
             $info['la']['raw']['flags'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 1));
             $offset += 1;
             $info['la']['flags']['seekable'] = (bool) ($info['la']['raw']['flags'] & 0x1);
             if ($info['la']['version'] >= 0.4) {
                 $info['la']['flags']['high_compression'] = (bool) ($info['la']['raw']['flags'] & 0x2);
             }
             $info['la']['original_crc'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
             $offset += 4;
             // mikeØbevin*de
             // Basically, the blocksize/seekevery are 61440/19 in La0.4 and 73728/16
             // in earlier versions. A seekpoint is added every blocksize * seekevery
             // samples, so 4 * int(totalSamples / (blockSize * seekEvery)) should
             // give the number of bytes used for the seekpoints. Of course, if seeking
             // is disabled, there are no seekpoints stored.
             if ($info['la']['version'] >= 0.4) {
                 $info['la']['blocksize'] = 61440;
                 $info['la']['seekevery'] = 19;
             } else {
                 $info['la']['blocksize'] = 73728;
                 $info['la']['seekevery'] = 16;
             }
             $info['la']['seekpoint_count'] = 0;
             if ($info['la']['flags']['seekable']) {
                 $info['la']['seekpoint_count'] = floor($info['la']['samples'] / ($info['la']['blocksize'] * $info['la']['seekevery']));
                 for ($i = 0; $i < $info['la']['seekpoint_count']; $i++) {
                     $info['la']['seekpoints'][] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
                     $offset += 4;
                 }
             }
             if ($info['la']['version'] >= 0.3) {
                 // Following the main header information, the program outputs all of the
                 // seekpoints. Following these is what I called the 'footer start',
                 // i.e. the position immediately after the La audio data is finished.
                 $info['la']['footerstart'] = Utils::LittleEndian2Int(substr($rawdata, $offset, 4));
                 $offset += 4;
                 if ($info['la']['footerstart'] > $info['filesize']) {
                     $info['warning'][] = 'FooterStart value points to offset ' . $info['la']['footerstart'] . ' which is beyond end-of-file (' . $info['filesize'] . ')';
                     $info['la']['footerstart'] = $info['filesize'];
                 }
             } else {
                 // La v0.2 didn't have FooterStart value
                 $info['la']['footerstart'] = $info['avdataend'];
             }
             if ($info['la']['footerstart'] < $info['avdataend']) {
                 if ($RIFFtempfilename = tempnam(Utils::getTempDirectory(), 'id3')) {
                     if ($RIFF_fp = fopen($RIFFtempfilename, 'w+b')) {
                         $RIFFdata = 'WAVE';
                         if ($info['la']['version'] == 0.2) {
                             $RIFFdata .= substr($rawdata, 12, 24);
                         } else {
                             $RIFFdata .= substr($rawdata, 16, 24);
                         }
                         if ($info['la']['footerstart'] < $info['avdataend']) {
                             $this->fseek($info['la']['footerstart']);
                             $RIFFdata .= $this->fread($info['avdataend'] - $info['la']['footerstart']);
                         }
                         $RIFFdata = 'RIFF' . Utils::LittleEndian2String(strlen($RIFFdata), 4, false) . $RIFFdata;
                         fwrite($RIFF_fp, $RIFFdata, strlen($RIFFdata));
                         fclose($RIFF_fp);
                         $getid3_temp = new GetID3();
                         $getid3_temp->openfile($RIFFtempfilename);
                         $getid3_riff = new Riff($getid3_temp);
                         $getid3_riff->Analyze();
                         if (empty($getid3_temp->info['error'])) {
                             $info['riff'] = $getid3_temp->info['riff'];
                         } else {
                             $info['warning'][] = 'Error parsing RIFF portion of La file: ' . implode($getid3_temp->info['error']);
                         }
                         unset($getid3_temp, $getid3_riff);
                     }
                     unlink($RIFFtempfilename);
                 }
             }
             // $info['avdataoffset'] should be zero to begin with, but just in case it's not, include the addition anyway
             $info['avdataend'] = $info['avdataoffset'] + $info['la']['footerstart'];
             $info['avdataoffset'] = $info['avdataoffset'] + $offset;
             $info['la']['compression_ratio'] = (double) (($info['avdataend'] - $info['avdataoffset']) / $info['la']['uncompressed_size']);
             $info['playtime_seconds'] = (double) ($info['la']['samples'] / $info['la']['sample_rate']) / $info['la']['channels'];
             if ($info['playtime_seconds'] == 0) {
                 $info['error'][] = 'Corrupt LA file: playtime_seconds == zero';
                 return false;
             }
             $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             //$info['audio']['codec']              = $info['la']['codec'];
             $info['audio']['bits_per_sample'] = $info['la']['bits_per_sample'];
             break;
         default:
             if (substr($rawdata, $offset, 2) == 'LA') {
                 $info['error'][] = 'This version of getID3() [' . $this->getid3->version() . '] does not support LA version ' . substr($rawdata, $offset + 2, 1) . '.' . substr($rawdata, $offset + 3, 1) . ' which this appears to be - check http://getid3.sourceforge.net for updates.';
             } else {
                 $info['error'][] = 'Not a LA (Lossless-Audio) file';
             }
             return false;
             break;
     }
     $info['audio']['channels'] = $info['la']['channels'];
     $info['audio']['sample_rate'] = (int) $info['la']['sample_rate'];
     $info['audio']['encoder'] = 'LA v' . $info['la']['version'];
     return true;
 }
Пример #17
0
 public function ParseDirectoryRecord($directorydata)
 {
     $info =& $this->getid3->info;
     if (isset($info['iso']['supplementary_volume_descriptor'])) {
         $TextEncoding = 'UTF-16BE';
         // Big-Endian Unicode
     } else {
         $TextEncoding = 'ISO-8859-1';
         // Latin-1
     }
     $this->fseek($directorydata['location_bytes']);
     $DirectoryRecordData = $this->fread(1);
     while (ord($DirectoryRecordData[0]) > 33) {
         $DirectoryRecordData .= $this->fread(ord($DirectoryRecordData[0]) - 1);
         $ThisDirectoryRecord['raw']['length'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 0, 1));
         $ThisDirectoryRecord['raw']['extended_attribute_length'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 1, 1));
         $ThisDirectoryRecord['raw']['offset_logical'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 2, 4));
         $ThisDirectoryRecord['raw']['filesize'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 10, 4));
         $ThisDirectoryRecord['raw']['recording_date_time'] = substr($DirectoryRecordData, 18, 7);
         $ThisDirectoryRecord['raw']['file_flags'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 25, 1));
         $ThisDirectoryRecord['raw']['file_unit_size'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 26, 1));
         $ThisDirectoryRecord['raw']['interleave_gap_size'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 27, 1));
         $ThisDirectoryRecord['raw']['volume_sequence_number'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 28, 2));
         $ThisDirectoryRecord['raw']['file_identifier_length'] = Utils::LittleEndian2Int(substr($DirectoryRecordData, 32, 1));
         $ThisDirectoryRecord['raw']['file_identifier'] = substr($DirectoryRecordData, 33, $ThisDirectoryRecord['raw']['file_identifier_length']);
         $ThisDirectoryRecord['file_identifier_ascii'] = Utils::iconv_fallback($TextEncoding, $info['encoding'], $ThisDirectoryRecord['raw']['file_identifier']);
         $ThisDirectoryRecord['filesize'] = $ThisDirectoryRecord['raw']['filesize'];
         $ThisDirectoryRecord['offset_bytes'] = $ThisDirectoryRecord['raw']['offset_logical'] * 2048;
         $ThisDirectoryRecord['file_flags']['hidden'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x1);
         $ThisDirectoryRecord['file_flags']['directory'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x2);
         $ThisDirectoryRecord['file_flags']['associated'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x4);
         $ThisDirectoryRecord['file_flags']['extended'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x8);
         $ThisDirectoryRecord['file_flags']['permissions'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x10);
         $ThisDirectoryRecord['file_flags']['multiple'] = (bool) ($ThisDirectoryRecord['raw']['file_flags'] & 0x80);
         $ThisDirectoryRecord['recording_timestamp'] = $this->ISOtime2UNIXtime($ThisDirectoryRecord['raw']['recording_date_time']);
         if ($ThisDirectoryRecord['file_flags']['directory']) {
             $ThisDirectoryRecord['filename'] = $directorydata['full_path'];
         } else {
             $ThisDirectoryRecord['filename'] = $directorydata['full_path'] . $this->ISOstripFilenameVersion($ThisDirectoryRecord['file_identifier_ascii']);
             $info['iso']['files'] = Utils::array_merge_clobber($info['iso']['files'], Utils::CreateDeepArray($ThisDirectoryRecord['filename'], '/', $ThisDirectoryRecord['filesize']));
         }
         $DirectoryRecord[] = $ThisDirectoryRecord;
         $DirectoryRecordData = $this->fread(1);
     }
     return $DirectoryRecord;
 }
Пример #18
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
     $FLVheader = $this->fread(5);
     $info['fileformat'] = 'flv';
     $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
     $info['flv']['header']['version'] = Utils::BigEndian2Int(substr($FLVheader, 3, 1));
     $TypeFlags = Utils::BigEndian2Int(substr($FLVheader, 4, 1));
     if ($info['flv']['header']['signature'] != self::magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes(self::magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($info['flv']['header']['signature']) . '"';
         unset($info['flv'], $info['fileformat']);
         return false;
     }
     $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x4);
     $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x1);
     $FrameSizeDataLength = Utils::BigEndian2Int($this->fread(4));
     $FLVheaderFrameLength = 9;
     if ($FrameSizeDataLength > $FLVheaderFrameLength) {
         $this->fseek($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 ($this->ftell() + 16 < $info['avdataend'] && ($tagParseCount++ <= $this->max_frames || !$found_valid_meta_playtime)) {
         $ThisTagHeader = $this->fread(16);
         $PreviousTagLength = Utils::BigEndian2Int(substr($ThisTagHeader, 0, 4));
         $TagType = Utils::BigEndian2Int(substr($ThisTagHeader, 4, 1));
         $DataLength = Utils::BigEndian2Int(substr($ThisTagHeader, 5, 3));
         $Timestamp = Utils::BigEndian2Int(substr($ThisTagHeader, 8, 3));
         $LastHeaderByte = Utils::BigEndian2Int(substr($ThisTagHeader, 15, 1));
         $NextOffset = $this->ftell() - 1 + $DataLength;
         if ($Timestamp > $Duration) {
             $Duration = $Timestamp;
         }
         $flv_framecount['total']++;
         switch ($TagType) {
             case self::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::TAG_VIDEO:
                 $flv_framecount['video']++;
                 if (!$found_video) {
                     $found_video = true;
                     $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x7;
                     $FLVvideoHeader = $this->fread(11);
                     if ($info['flv']['video']['videoCodec'] === self::VIDEO_H264) {
                         // this code block contributed by: moysevichØgmail*com
                         $AVCPacketType = Utils::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
                         if ($AVCPacketType == H264_AVC_SEQUENCE_HEADER) {
                             //	read AVCDecoderConfigurationRecord
                             $configurationVersion = Utils::BigEndian2Int(substr($FLVvideoHeader, 4, 1));
                             $AVCProfileIndication = Utils::BigEndian2Int(substr($FLVvideoHeader, 5, 1));
                             $profile_compatibility = Utils::BigEndian2Int(substr($FLVvideoHeader, 6, 1));
                             $lengthSizeMinusOne = Utils::BigEndian2Int(substr($FLVvideoHeader, 7, 1));
                             $numOfSequenceParameterSets = Utils::BigEndian2Int(substr($FLVvideoHeader, 8, 1));
                             if (($numOfSequenceParameterSets & 0x1f) != 0) {
                                 //	there is at least one SequenceParameterSet
                                 //	read size of the first SequenceParameterSet
                                 //$spsSize = Utils::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
                                 $spsSize = Utils::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
                                 //	read the first SequenceParameterSet
                                 $sps = $this->fread($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::VIDEO_H263) {
                         $PictureSizeType = Utils::BigEndian2Int(substr($FLVvideoHeader, 3, 2)) >> 7;
                         $PictureSizeType = $PictureSizeType & 0x7;
                         $info['flv']['header']['videoSizeType'] = $PictureSizeType;
                         switch ($PictureSizeType) {
                             case 0:
                                 //$PictureSizeEnc = Utils::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
                                 //$PictureSizeEnc <<= 1;
                                 //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
                                 //$PictureSizeEnc = Utils::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
                                 //$PictureSizeEnc <<= 1;
                                 //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
                                 $PictureSizeEnc['x'] = Utils::BigEndian2Int(substr($FLVvideoHeader, 4, 2)) >> 7;
                                 $PictureSizeEnc['y'] = Utils::BigEndian2Int(substr($FLVvideoHeader, 5, 2)) >> 7;
                                 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xff;
                                 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xff;
                                 break;
                             case 1:
                                 $PictureSizeEnc['x'] = Utils::BigEndian2Int(substr($FLVvideoHeader, 4, 3)) >> 7;
                                 $PictureSizeEnc['y'] = Utils::BigEndian2Int(substr($FLVvideoHeader, 6, 3)) >> 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;
                         }
                     } elseif ($info['flv']['video']['videoCodec'] === self::VIDEO_VP6FLV_ALPHA) {
                         /* contributed by schouwerwouØgmail*com */
                         if (!isset($info['video']['resolution_x'])) {
                             // only when meta data isn't set
                             $PictureSizeEnc['x'] = Utils::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
                             $PictureSizeEnc['y'] = Utils::BigEndian2Int(substr($FLVvideoHeader, 7, 2));
                             $info['video']['resolution_x'] = ($PictureSizeEnc['x'] & 0xff) << 3;
                             $info['video']['resolution_y'] = ($PictureSizeEnc['y'] & 0xff) << 3;
                         }
                         /* end schouwerwouØgmail*com */
                     }
                     if (!empty($info['video']['resolution_x']) && !empty($info['video']['resolution_y'])) {
                         $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
                     }
                 }
                 break;
                 // Meta tag
             // Meta tag
             case self::TAG_META:
                 if (!$found_meta) {
                     $found_meta = true;
                     $this->fseek(-1, SEEK_CUR);
                     $datachunk = $this->fread($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] = Utils::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;
         }
         $this->fseek($NextOffset);
     }
     $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'] = self::audioFormatLookup($info['flv']['audio']['audioFormat']);
         $info['audio']['sample_rate'] = self::audioRateLookup($info['flv']['audio']['audioRate']);
         $info['audio']['bits_per_sample'] = self::audioBitDepthLookup($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'] = self::videoCodecLookup($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'] = self::audioFormatLookup($info['flv']['meta']['onMetaData']['audiocodecid']);
     }
     if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
         $info['video']['codec'] = self::videoCodecLookup($info['flv']['meta']['onMetaData']['videocodecid']);
     }
     return true;
 }
Пример #19
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     ///AH
     $info['ac3']['raw']['bsi'] = array();
     $thisfile_ac3 =& $info['ac3'];
     $thisfile_ac3_raw =& $thisfile_ac3['raw'];
     $thisfile_ac3_raw_bsi =& $thisfile_ac3_raw['bsi'];
     // http://www.atsc.org/standards/a_52a.pdf
     $info['fileformat'] = 'ac3';
     // An AC-3 serial coded audio bit stream is made up of a sequence of synchronization frames
     // Each synchronization frame contains 6 coded audio blocks (AB), each of which represent 256
     // new audio samples per channel. A synchronization information (SI) header at the beginning
     // of each frame contains information needed to acquire and maintain synchronization. A
     // bit stream information (BSI) header follows SI, and contains parameters describing the coded
     // audio service. The coded audio blocks may be followed by an auxiliary data (Aux) field. At the
     // end of each frame is an error check field that includes a CRC word for error detection. An
     // additional CRC word is located in the SI header, the use of which, by a decoder, is optional.
     //
     // syncinfo() | bsi() | AB0 | AB1 | AB2 | AB3 | AB4 | AB5 | Aux | CRC
     // syncinfo() {
     // 	 syncword    16
     // 	 crc1        16
     // 	 fscod        2
     // 	 frmsizecod   6
     // } /* end of syncinfo */
     $this->fseek($info['avdataoffset']);
     $this->AC3header['syncinfo'] = $this->fread(5);
     if (strpos($this->AC3header['syncinfo'], self::syncword) === 0) {
         $thisfile_ac3_raw['synchinfo']['synchword'] = self::syncword;
         $offset = 2;
     } else {
         if (!$this->isDependencyFor('Matroska')) {
             unset($info['fileformat'], $info['ac3']);
             return $this->error('Expecting "' . Utils::PrintHexBytes(self::syncword) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($this->AC3header['syncinfo'], 0, 2)) . '"');
         }
         $offset = 0;
         $this->fseek(-2, SEEK_CUR);
     }
     $info['audio']['dataformat'] = 'ac3';
     $info['audio']['bitrate_mode'] = 'cbr';
     $info['audio']['lossless'] = false;
     $thisfile_ac3_raw['synchinfo']['crc1'] = Utils::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset, 2));
     $ac3_synchinfo_fscod_frmsizecod = Utils::LittleEndian2Int(substr($this->AC3header['syncinfo'], $offset + 2, 1));
     $thisfile_ac3_raw['synchinfo']['fscod'] = ($ac3_synchinfo_fscod_frmsizecod & 0xc0) >> 6;
     $thisfile_ac3_raw['synchinfo']['frmsizecod'] = $ac3_synchinfo_fscod_frmsizecod & 0x3f;
     $thisfile_ac3['sample_rate'] = self::sampleRateCodeLookup($thisfile_ac3_raw['synchinfo']['fscod']);
     if ($thisfile_ac3_raw['synchinfo']['fscod'] <= 3) {
         $info['audio']['sample_rate'] = $thisfile_ac3['sample_rate'];
     }
     $thisfile_ac3['frame_length'] = self::frameSizeLookup($thisfile_ac3_raw['synchinfo']['frmsizecod'], $thisfile_ac3_raw['synchinfo']['fscod']);
     $thisfile_ac3['bitrate'] = self::bitrateLookup($thisfile_ac3_raw['synchinfo']['frmsizecod']);
     $info['audio']['bitrate'] = $thisfile_ac3['bitrate'];
     $this->AC3header['bsi'] = Utils::BigEndian2Bin($this->fread(15));
     $ac3_bsi_offset = 0;
     $thisfile_ac3_raw_bsi['bsid'] = $this->readHeaderBSI(5);
     if ($thisfile_ac3_raw_bsi['bsid'] > 8) {
         // Decoders which can decode version 8 will thus be able to decode version numbers less than 8.
         // If this standard is extended by the addition of additional elements or features, a value of bsid greater than 8 will be used.
         // Decoders built to this version of the standard will not be able to decode versions with bsid greater than 8.
         $this->error('Bit stream identification is version ' . $thisfile_ac3_raw_bsi['bsid'] . ', but getID3() only understands up to version 8');
         unset($info['ac3']);
         return false;
     }
     $thisfile_ac3_raw_bsi['bsmod'] = $this->readHeaderBSI(3);
     $thisfile_ac3_raw_bsi['acmod'] = $this->readHeaderBSI(3);
     $thisfile_ac3['service_type'] = self::serviceTypeLookup($thisfile_ac3_raw_bsi['bsmod'], $thisfile_ac3_raw_bsi['acmod']);
     $ac3_coding_mode = self::audioCodingModeLookup($thisfile_ac3_raw_bsi['acmod']);
     foreach ($ac3_coding_mode as $key => $value) {
         $thisfile_ac3[$key] = $value;
     }
     switch ($thisfile_ac3_raw_bsi['acmod']) {
         case 0:
         case 1:
             $info['audio']['channelmode'] = 'mono';
             break;
         case 3:
         case 4:
             $info['audio']['channelmode'] = 'stereo';
             break;
         default:
             $info['audio']['channelmode'] = 'surround';
             break;
     }
     $info['audio']['channels'] = $thisfile_ac3['num_channels'];
     if ($thisfile_ac3_raw_bsi['acmod'] & 0x1) {
         // If the lsb of acmod is a 1, center channel is in use and cmixlev follows in the bit stream.
         $thisfile_ac3_raw_bsi['cmixlev'] = $this->readHeaderBSI(2);
         $thisfile_ac3['center_mix_level'] = self::centerMixLevelLookup($thisfile_ac3_raw_bsi['cmixlev']);
     }
     if ($thisfile_ac3_raw_bsi['acmod'] & 0x4) {
         // If the msb of acmod is a 1, surround channels are in use and surmixlev follows in the bit stream.
         $thisfile_ac3_raw_bsi['surmixlev'] = $this->readHeaderBSI(2);
         $thisfile_ac3['surround_mix_level'] = self::surroundMixLevelLookup($thisfile_ac3_raw_bsi['surmixlev']);
     }
     if ($thisfile_ac3_raw_bsi['acmod'] == 0x2) {
         // When operating in the two channel mode, this 2-bit code indicates whether or not the program has been encoded in Dolby Surround.
         $thisfile_ac3_raw_bsi['dsurmod'] = $this->readHeaderBSI(2);
         $thisfile_ac3['dolby_surround_mode'] = self::dolbySurroundModeLookup($thisfile_ac3_raw_bsi['dsurmod']);
     }
     $thisfile_ac3_raw_bsi['lfeon'] = (bool) $this->readHeaderBSI(1);
     $thisfile_ac3['lfe_enabled'] = $thisfile_ac3_raw_bsi['lfeon'];
     if ($thisfile_ac3_raw_bsi['lfeon']) {
         //$info['audio']['channels']++;
         $info['audio']['channels'] .= '.1';
     }
     $thisfile_ac3['channels_enabled'] = self::channelsEnabledLookup($thisfile_ac3_raw_bsi['acmod'], $thisfile_ac3_raw_bsi['lfeon']);
     // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
     // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
     $thisfile_ac3_raw_bsi['dialnorm'] = $this->readHeaderBSI(5);
     $thisfile_ac3['dialogue_normalization'] = '-' . $thisfile_ac3_raw_bsi['dialnorm'] . 'dB';
     $thisfile_ac3_raw_bsi['compre_flag'] = (bool) $this->readHeaderBSI(1);
     if ($thisfile_ac3_raw_bsi['compre_flag']) {
         $thisfile_ac3_raw_bsi['compr'] = $this->readHeaderBSI(8);
         $thisfile_ac3['heavy_compression'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr']);
     }
     $thisfile_ac3_raw_bsi['langcode_flag'] = (bool) $this->readHeaderBSI(1);
     if ($thisfile_ac3_raw_bsi['langcode_flag']) {
         $thisfile_ac3_raw_bsi['langcod'] = $this->readHeaderBSI(8);
     }
     $thisfile_ac3_raw_bsi['audprodie'] = (bool) $this->readHeaderBSI(1);
     if ($thisfile_ac3_raw_bsi['audprodie']) {
         $thisfile_ac3_raw_bsi['mixlevel'] = $this->readHeaderBSI(5);
         $thisfile_ac3_raw_bsi['roomtyp'] = $this->readHeaderBSI(2);
         $thisfile_ac3['mixing_level'] = 80 + $thisfile_ac3_raw_bsi['mixlevel'] . 'dB';
         $thisfile_ac3['room_type'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp']);
     }
     if ($thisfile_ac3_raw_bsi['acmod'] == 0x0) {
         // If acmod is 0, then two completely independent program channels (dual mono)
         // are encoded into the bit stream, and are referenced as Ch1, Ch2. In this case,
         // a number of additional items are present in BSI or audblk to fully describe Ch2.
         // This indicates how far the average dialogue level is below digital 100 percent. Valid values are 1-31.
         // The value of 0 is reserved. The values of 1 to 31 are interpreted as -1 dB to -31 dB with respect to digital 100 percent.
         $thisfile_ac3_raw_bsi['dialnorm2'] = $this->readHeaderBSI(5);
         $thisfile_ac3['dialogue_normalization2'] = '-' . $thisfile_ac3_raw_bsi['dialnorm2'] . 'dB';
         $thisfile_ac3_raw_bsi['compre_flag2'] = (bool) $this->readHeaderBSI(1);
         if ($thisfile_ac3_raw_bsi['compre_flag2']) {
             $thisfile_ac3_raw_bsi['compr2'] = $this->readHeaderBSI(8);
             $thisfile_ac3['heavy_compression2'] = self::heavyCompression($thisfile_ac3_raw_bsi['compr2']);
         }
         $thisfile_ac3_raw_bsi['langcode_flag2'] = (bool) $this->readHeaderBSI(1);
         if ($thisfile_ac3_raw_bsi['langcode_flag2']) {
             $thisfile_ac3_raw_bsi['langcod2'] = $this->readHeaderBSI(8);
         }
         $thisfile_ac3_raw_bsi['audprodie2'] = (bool) $this->readHeaderBSI(1);
         if ($thisfile_ac3_raw_bsi['audprodie2']) {
             $thisfile_ac3_raw_bsi['mixlevel2'] = $this->readHeaderBSI(5);
             $thisfile_ac3_raw_bsi['roomtyp2'] = $this->readHeaderBSI(2);
             $thisfile_ac3['mixing_level2'] = 80 + $thisfile_ac3_raw_bsi['mixlevel2'] . 'dB';
             $thisfile_ac3['room_type2'] = self::roomTypeLookup($thisfile_ac3_raw_bsi['roomtyp2']);
         }
     }
     $thisfile_ac3_raw_bsi['copyright'] = (bool) $this->readHeaderBSI(1);
     $thisfile_ac3_raw_bsi['original'] = (bool) $this->readHeaderBSI(1);
     $thisfile_ac3_raw_bsi['timecode1_flag'] = (bool) $this->readHeaderBSI(1);
     if ($thisfile_ac3_raw_bsi['timecode1_flag']) {
         $thisfile_ac3_raw_bsi['timecode1'] = $this->readHeaderBSI(14);
     }
     $thisfile_ac3_raw_bsi['timecode2_flag'] = (bool) $this->readHeaderBSI(1);
     if ($thisfile_ac3_raw_bsi['timecode2_flag']) {
         $thisfile_ac3_raw_bsi['timecode2'] = $this->readHeaderBSI(14);
     }
     $thisfile_ac3_raw_bsi['addbsi_flag'] = (bool) $this->readHeaderBSI(1);
     if ($thisfile_ac3_raw_bsi['addbsi_flag']) {
         $thisfile_ac3_raw_bsi['addbsi_length'] = $this->readHeaderBSI(6);
         $this->AC3header['bsi'] .= Utils::BigEndian2Bin($this->fread($thisfile_ac3_raw_bsi['addbsi_length']));
         $thisfile_ac3_raw_bsi['addbsi_data'] = substr($this->AC3header['bsi'], $this->BSIoffset, $thisfile_ac3_raw_bsi['addbsi_length'] * 8);
         $this->BSIoffset += $thisfile_ac3_raw_bsi['addbsi_length'] * 8;
     }
     return true;
 }
Пример #20
0
 public function ASF_WMpicture(&$data)
 {
     //typedef struct _WMPicture{
     //  LPWSTR  pwszMIMEType;
     //  BYTE  bPictureType;
     //  LPWSTR  pwszDescription;
     //  DWORD  dwDataLen;
     //  BYTE*  pbData;
     //} WM_PICTURE;
     $WMpicture = array();
     $offset = 0;
     $WMpicture['image_type_id'] = Utils::LittleEndian2Int(substr($data, $offset, 1));
     $offset += 1;
     $WMpicture['image_type'] = self::WMpictureTypeLookup($WMpicture['image_type_id']);
     $WMpicture['image_size'] = Utils::LittleEndian2Int(substr($data, $offset, 4));
     $offset += 4;
     $WMpicture['image_mime'] = '';
     do {
         $next_byte_pair = substr($data, $offset, 2);
         $offset += 2;
         $WMpicture['image_mime'] .= $next_byte_pair;
     } while ($next_byte_pair !== "");
     $WMpicture['image_description'] = '';
     do {
         $next_byte_pair = substr($data, $offset, 2);
         $offset += 2;
         $WMpicture['image_description'] .= $next_byte_pair;
     } while ($next_byte_pair !== "");
     $WMpicture['dataoffset'] = $offset;
     $WMpicture['data'] = substr($data, $offset);
     $imageinfo = array();
     $WMpicture['image_mime'] = '';
     $imagechunkcheck = Utils::GetDataImageSize($WMpicture['data'], $imageinfo);
     unset($imageinfo);
     if (!empty($imagechunkcheck)) {
         $WMpicture['image_mime'] = image_type_to_mime_type($imagechunkcheck[2]);
     }
     if (!isset($this->getid3->info['asf']['comments']['picture'])) {
         $this->getid3->info['asf']['comments']['picture'] = array();
     }
     $this->getid3->info['asf']['comments']['picture'][] = array('data' => $WMpicture['data'], 'image_mime' => $WMpicture['image_mime']);
     return $WMpicture;
 }
Пример #21
0
 public function ZIPparseEndOfCentralDirectory()
 {
     $EndOfCentralDirectory['offset'] = $this->ftell();
     $ZIPendOfCentralDirectory = $this->fread(22);
     $EndOfCentralDirectory['signature'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 0, 4));
     if ($EndOfCentralDirectory['signature'] != 0x6054b50) {
         // invalid End Of Central Directory Signature
         $this->fseek($EndOfCentralDirectory['offset']);
         // seek back to where filepointer originally was so it can be handled properly
         return false;
     }
     $EndOfCentralDirectory['disk_number_current'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 4, 2));
     $EndOfCentralDirectory['disk_number_start_directory'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 6, 2));
     $EndOfCentralDirectory['directory_entries_this_disk'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 8, 2));
     $EndOfCentralDirectory['directory_entries_total'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 10, 2));
     $EndOfCentralDirectory['directory_size'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 12, 4));
     $EndOfCentralDirectory['directory_offset'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 16, 4));
     $EndOfCentralDirectory['comment_length'] = Utils::LittleEndian2Int(substr($ZIPendOfCentralDirectory, 20, 2));
     if ($EndOfCentralDirectory['comment_length'] > 0) {
         $EndOfCentralDirectory['comment'] = $this->fread($EndOfCentralDirectory['comment_length']);
     }
     return $EndOfCentralDirectory;
 }
Пример #22
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $this->fseek($info['avdataoffset']);
     $ShortenHeader = $this->fread(8);
     $magic = 'ajkg';
     if (substr($ShortenHeader, 0, 4) != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes(substr($ShortenHeader, 0, 4)) . '"';
         return false;
     }
     $info['fileformat'] = 'shn';
     $info['audio']['dataformat'] = 'shn';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['shn']['version'] = Utils::LittleEndian2Int(substr($ShortenHeader, 4, 1));
     $this->fseek($info['avdataend'] - 12);
     $SeekTableSignatureTest = $this->fread(12);
     $info['shn']['seektable']['present'] = (bool) (substr($SeekTableSignatureTest, 4, 8) == 'SHNAMPSK');
     if ($info['shn']['seektable']['present']) {
         $info['shn']['seektable']['length'] = Utils::LittleEndian2Int(substr($SeekTableSignatureTest, 0, 4));
         $info['shn']['seektable']['offset'] = $info['avdataend'] - $info['shn']['seektable']['length'];
         $this->fseek($info['shn']['seektable']['offset']);
         $SeekTableMagic = $this->fread(4);
         $magic = 'SEEK';
         if ($SeekTableMagic != $magic) {
             $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['shn']['seektable']['offset'] . ', found "' . Utils::PrintHexBytes($SeekTableMagic) . '"';
             return false;
         } else {
             // typedef struct tag_TSeekEntry
             // {
             //   unsigned long SampleNumber;
             //   unsigned long SHNFileByteOffset;
             //   unsigned long SHNLastBufferReadPosition;
             //   unsigned short SHNByteGet;
             //   unsigned short SHNBufferOffset;
             //   unsigned short SHNFileBitOffset;
             //   unsigned long SHNGBuffer;
             //   unsigned short SHNBitShift;
             //   long CBuf0[3];
             //   long CBuf1[3];
             //   long Offset0[4];
             //   long Offset1[4];
             // }TSeekEntry;
             $SeekTableData = $this->fread($info['shn']['seektable']['length'] - 16);
             $info['shn']['seektable']['entry_count'] = floor(strlen($SeekTableData) / 80);
             //$info['shn']['seektable']['entries'] = array();
             //$SeekTableOffset = 0;
             //for ($i = 0; $i < $info['shn']['seektable']['entry_count']; $i++) {
             //	$SeekTableEntry['sample_number'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_file_byte_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_last_buffer_read_position'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_byte_get'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	$SeekTableEntry['shn_buffer_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	$SeekTableEntry['shn_file_bit_offset'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	$SeekTableEntry['shn_gbuffer'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //	$SeekTableOffset += 4;
             //	$SeekTableEntry['shn_bit_shift'] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 2));
             //	$SeekTableOffset += 2;
             //	for ($j = 0; $j < 3; $j++) {
             //		$SeekTableEntry['cbuf0'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //	for ($j = 0; $j < 3; $j++) {
             //		$SeekTableEntry['cbuf1'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //	for ($j = 0; $j < 4; $j++) {
             //		$SeekTableEntry['offset0'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //	for ($j = 0; $j < 4; $j++) {
             //		$SeekTableEntry['offset1'][$j] = Utils::LittleEndian2Int(substr($SeekTableData, $SeekTableOffset, 4));
             //		$SeekTableOffset += 4;
             //	}
             //
             //	$info['shn']['seektable']['entries'][] = $SeekTableEntry;
             //}
         }
     }
     if (Utils::isWindows()) {
         $RequiredFiles = array('shorten.exe', 'cygwin1.dll', 'head.exe');
         foreach ($RequiredFiles as $required_file) {
             if (!is_readable(Utils::getHelperAppDirectory() . $required_file)) {
                 $info['error'][] = Utils::getHelperAppDirectory() . $required_file . ' does not exist';
                 return false;
             }
         }
         $commandline = Utils::getHelperAppDirectory() . 'shorten.exe -x "' . $info['filenamepath'] . '" - | ' . Utils::getHelperAppDirectory() . 'head.exe -c 64';
         $commandline = str_replace('/', '\\', $commandline);
     } else {
         static $shorten_present;
         if (!isset($shorten_present)) {
             $shorten_present = file_exists('/usr/local/bin/shorten') || `which shorten`;
         }
         if (!$shorten_present) {
             $info['error'][] = 'shorten binary was not found in path or /usr/local/bin';
             return false;
         }
         $commandline = (file_exists('/usr/local/bin/shorten') ? '/usr/local/bin/' : '') . 'shorten -x ' . escapeshellarg($info['filenamepath']) . ' - | head -c 64';
     }
     $output = `{$commandline}`;
     if (!empty($output) && substr($output, 12, 4) == 'fmt ') {
         $fmt_size = Utils::LittleEndian2Int(substr($output, 16, 4));
         $DecodedWAVFORMATEX = Riff::parseWAVEFORMATex(substr($output, 20, $fmt_size));
         $info['audio']['channels'] = $DecodedWAVFORMATEX['channels'];
         $info['audio']['bits_per_sample'] = $DecodedWAVFORMATEX['bits_per_sample'];
         $info['audio']['sample_rate'] = $DecodedWAVFORMATEX['sample_rate'];
         if (substr($output, 20 + $fmt_size, 4) == 'data') {
             $info['playtime_seconds'] = Utils::LittleEndian2Int(substr($output, 20 + 4 + $fmt_size, 4)) / $DecodedWAVFORMATEX['raw']['nAvgBytesPerSec'];
         } else {
             $info['error'][] = 'shorten failed to decode DATA chunk to expected location, cannot determine playtime';
             return false;
         }
         $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) / $info['playtime_seconds'] * 8;
     } else {
         $info['error'][] = 'shorten failed to decode file to WAV for parsing';
         return false;
     }
     return true;
 }
Пример #23
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'swf';
     $info['video']['dataformat'] = 'swf';
     // http://www.openswf.org/spec/SWFfileformat.html
     $this->fseek($info['avdataoffset']);
     $SWFfileData = $this->fread($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 "' . Utils::PrintHexBytes($info['swf']['header']['signature']) . '"';
             unset($info['swf']);
             unset($info['fileformat']);
             return false;
             break;
     }
     $info['swf']['header']['version'] = Utils::LittleEndian2Int(substr($SWFfileData, 3, 1));
     $info['swf']['header']['length'] = Utils::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'] = Utils::Bin2Dec($X2);
     $info['swf']['header']['frame_height'] = Utils::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'] = Utils::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
     $info['swf']['header']['frame_count'] = Utils::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 = Utils::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
         $TagID = ($TagIDTagLength & 0xfffc) >> 6;
         $TagLength = $TagIDTagLength & 0x3f;
         $CurrentOffset += 2;
         if ($TagLength == 0x3f) {
             $TagLength = Utils::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(Utils::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
                 break;
             default:
                 if ($this->ReturnAllTagData) {
                     $info['swf']['tags'][] = $TagData;
                 }
                 break;
         }
         $CurrentOffset += $TagLength;
     }
     return true;
 }
Пример #24
0
 private function EitherEndian2Int($byteword, $signed = false)
 {
     if ($this->container == 'riff') {
         return Utils::LittleEndian2Int($byteword, $signed);
     }
     return Utils::BigEndian2Int($byteword, false, $signed);
 }
Пример #25
0
 public function Analyze()
 {
     $info =& $this->getid3->info;
     // shortcuts
     $info['bmp']['header']['raw'] = array();
     $thisfile_bmp =& $info['bmp'];
     $thisfile_bmp_header =& $thisfile_bmp['header'];
     $thisfile_bmp_header_raw =& $thisfile_bmp_header['raw'];
     // BITMAPFILEHEADER [14 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_62uq.asp
     // all versions
     // WORD    bfType;
     // DWORD   bfSize;
     // WORD    bfReserved1;
     // WORD    bfReserved2;
     // DWORD   bfOffBits;
     $this->fseek($info['avdataoffset']);
     $offset = 0;
     $BMPheader = $this->fread(14 + 40);
     $thisfile_bmp_header_raw['identifier'] = substr($BMPheader, $offset, 2);
     $offset += 2;
     $magic = 'BM';
     if ($thisfile_bmp_header_raw['identifier'] != $magic) {
         $info['error'][] = 'Expecting "' . Utils::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Utils::PrintHexBytes($thisfile_bmp_header_raw['identifier']) . '"';
         unset($info['fileformat']);
         unset($info['bmp']);
         return false;
     }
     $thisfile_bmp_header_raw['filesize'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     $thisfile_bmp_header_raw['reserved1'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
     $offset += 2;
     $thisfile_bmp_header_raw['reserved2'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
     $offset += 2;
     $thisfile_bmp_header_raw['data_offset'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     $thisfile_bmp_header_raw['header_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     // check if the hardcoded-to-1 "planes" is at offset 22 or 26
     $planes22 = Utils::LittleEndian2Int(substr($BMPheader, 22, 2));
     $planes26 = Utils::LittleEndian2Int(substr($BMPheader, 26, 2));
     if ($planes22 == 1 && $planes26 != 1) {
         $thisfile_bmp['type_os'] = 'OS/2';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($planes26 == 1 && $planes22 != 1) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 12) {
         $thisfile_bmp['type_os'] = 'OS/2';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 40) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 1;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 84) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 4;
     } elseif ($thisfile_bmp_header_raw['header_size'] == 100) {
         $thisfile_bmp['type_os'] = 'Windows';
         $thisfile_bmp['type_version'] = 5;
     } else {
         $info['error'][] = 'Unknown BMP subtype (or not a BMP file)';
         unset($info['fileformat']);
         unset($info['bmp']);
         return false;
     }
     $info['fileformat'] = 'bmp';
     $info['video']['dataformat'] = 'bmp';
     $info['video']['lossless'] = true;
     $info['video']['pixel_aspect_ratio'] = (double) 1;
     if ($thisfile_bmp['type_os'] == 'OS/2') {
         // OS/2-format BMP
         // http://netghost.narod.ru/gff/graphics/summary/os2bmp.htm
         // DWORD  Size;             /* Size of this structure in bytes */
         // DWORD  Width;            /* Bitmap width in pixels */
         // DWORD  Height;           /* Bitmap height in pixel */
         // WORD   NumPlanes;        /* Number of bit planes (color depth) */
         // WORD   BitsPerPixel;     /* Number of bits per pixel per plane */
         $thisfile_bmp_header_raw['width'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['height'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['planes'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['bits_per_pixel'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
         $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
         $info['video']['codec'] = 'BI_RGB ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit';
         $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
         if ($thisfile_bmp['type_version'] >= 2) {
             // DWORD  Compression;      /* Bitmap compression scheme */
             // DWORD  ImageDataSize;    /* Size of bitmap data in bytes */
             // DWORD  XResolution;      /* X resolution of display device */
             // DWORD  YResolution;      /* Y resolution of display device */
             // DWORD  ColorsUsed;       /* Number of color table indices used */
             // DWORD  ColorsImportant;  /* Number of important color indices */
             // WORD   Units;            /* Type of units used to measure resolution */
             // WORD   Reserved;         /* Pad structure to 4-byte boundary */
             // WORD   Recording;        /* Recording algorithm */
             // WORD   Rendering;        /* Halftoning algorithm used */
             // DWORD  Size1;            /* Reserved for halftoning algorithm use */
             // DWORD  Size2;            /* Reserved for halftoning algorithm use */
             // DWORD  ColorEncoding;    /* Color model used in bitmap */
             // DWORD  Identifier;       /* Reserved for application use */
             $thisfile_bmp_header_raw['compression'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['bmp_data_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_h'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_v'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['colors_used'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['colors_important'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_units'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['reserved1'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['recording'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['rendering'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['size1'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['size2'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['color_encoding'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['identifier'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header['compression'] = $this->BMPcompressionOS2Lookup($thisfile_bmp_header_raw['compression']);
             $info['video']['codec'] = $thisfile_bmp_header['compression'] . ' ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit';
         }
     } elseif ($thisfile_bmp['type_os'] == 'Windows') {
         // Windows-format BMP
         // BITMAPINFOHEADER - [40 bytes] http://msdn.microsoft.com/library/en-us/gdi/bitmaps_1rw2.asp
         // all versions
         // DWORD  biSize;
         // LONG   biWidth;
         // LONG   biHeight;
         // WORD   biPlanes;
         // WORD   biBitCount;
         // DWORD  biCompression;
         // DWORD  biSizeImage;
         // LONG   biXPelsPerMeter;
         // LONG   biYPelsPerMeter;
         // DWORD  biClrUsed;
         // DWORD  biClrImportant;
         // possibly integrate this section and module.audio-video.riff.php::ParseBITMAPINFOHEADER() ?
         $thisfile_bmp_header_raw['width'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['height'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['planes'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['bits_per_pixel'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['compression'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['bmp_data_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['resolution_h'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['resolution_v'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['colors_used'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['colors_important'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header['compression'] = $this->BMPcompressionWindowsLookup($thisfile_bmp_header_raw['compression']);
         $info['video']['resolution_x'] = $thisfile_bmp_header_raw['width'];
         $info['video']['resolution_y'] = $thisfile_bmp_header_raw['height'];
         $info['video']['codec'] = $thisfile_bmp_header['compression'] . ' ' . $thisfile_bmp_header_raw['bits_per_pixel'] . '-bit';
         $info['video']['bits_per_sample'] = $thisfile_bmp_header_raw['bits_per_pixel'];
         if ($thisfile_bmp['type_version'] >= 4 || $thisfile_bmp_header_raw['compression'] == 3) {
             // should only be v4+, but BMPs with type_version==1 and BI_BITFIELDS compression have been seen
             $BMPheader .= $this->fread(44);
             // BITMAPV4HEADER - [44 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_2k1e.asp
             // Win95+, WinNT4.0+
             // DWORD        bV4RedMask;
             // DWORD        bV4GreenMask;
             // DWORD        bV4BlueMask;
             // DWORD        bV4AlphaMask;
             // DWORD        bV4CSType;
             // CIEXYZTRIPLE bV4Endpoints;
             // DWORD        bV4GammaRed;
             // DWORD        bV4GammaGreen;
             // DWORD        bV4GammaBlue;
             $thisfile_bmp_header_raw['red_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['green_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['blue_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['alpha_mask'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['cs_type'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['ciexyz_red'] = substr($BMPheader, $offset, 4);
             $offset += 4;
             $thisfile_bmp_header_raw['ciexyz_green'] = substr($BMPheader, $offset, 4);
             $offset += 4;
             $thisfile_bmp_header_raw['ciexyz_blue'] = substr($BMPheader, $offset, 4);
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_red'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_green'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_blue'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header['ciexyz_red'] = Utils::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
             $thisfile_bmp_header['ciexyz_green'] = Utils::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
             $thisfile_bmp_header['ciexyz_blue'] = Utils::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
         }
         if ($thisfile_bmp['type_version'] >= 5) {
             $BMPheader .= $this->fread(16);
             // BITMAPV5HEADER - [16 bytes] - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_7c36.asp
             // Win98+, Win2000+
             // DWORD        bV5Intent;
             // DWORD        bV5ProfileData;
             // DWORD        bV5ProfileSize;
             // DWORD        bV5Reserved;
             $thisfile_bmp_header_raw['intent'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['profile_data_offset'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['profile_data_size'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['reserved3'] = Utils::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
         }
     } else {
         $info['error'][] = 'Unknown BMP format in header.';
         return false;
     }
     if ($this->ExtractPalette || $this->ExtractData) {
         $PaletteEntries = 0;
         if ($thisfile_bmp_header_raw['bits_per_pixel'] < 16) {
             $PaletteEntries = pow(2, $thisfile_bmp_header_raw['bits_per_pixel']);
         } elseif (isset($thisfile_bmp_header_raw['colors_used']) && $thisfile_bmp_header_raw['colors_used'] > 0 && $thisfile_bmp_header_raw['colors_used'] <= 256) {
             $PaletteEntries = $thisfile_bmp_header_raw['colors_used'];
         }
         if ($PaletteEntries > 0) {
             $BMPpalette = $this->fread(4 * $PaletteEntries);
             $paletteoffset = 0;
             for ($i = 0; $i < $PaletteEntries; $i++) {
                 // RGBQUAD          - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_5f8y.asp
                 // BYTE    rgbBlue;
                 // BYTE    rgbGreen;
                 // BYTE    rgbRed;
                 // BYTE    rgbReserved;
                 $blue = Utils::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 $green = Utils::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 $red = Utils::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 if ($thisfile_bmp['type_os'] == 'OS/2' && $thisfile_bmp['type_version'] == 1) {
                     // no padding byte
                 } else {
                     $paletteoffset++;
                     // padding byte
                 }
                 $thisfile_bmp['palette'][$i] = $red << 16 | $green << 8 | $blue;
             }
         }
     }
     if ($this->ExtractData) {
         $this->fseek($thisfile_bmp_header_raw['data_offset']);
         $RowByteLength = ceil($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8) / 4) * 4;
         // round up to nearest DWORD boundry
         $BMPpixelData = $this->fread($thisfile_bmp_header_raw['height'] * $RowByteLength);
         $pixeldataoffset = 0;
         $thisfile_bmp_header_raw['compression'] = isset($thisfile_bmp_header_raw['compression']) ? $thisfile_bmp_header_raw['compression'] : '';
         switch ($thisfile_bmp_header_raw['compression']) {
             case 0:
                 // BI_RGB
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 1:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
                                 $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
                                 for ($i = 7; $i >= 0; $i--) {
                                     $paletteindex = ($paletteindexbyte & 0x1 << $i) >> $i;
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                     $col++;
                                 }
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 4:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col = $col) {
                                 $paletteindexbyte = ord($BMPpixelData[$pixeldataoffset++]);
                                 for ($i = 1; $i >= 0; $i--) {
                                     $paletteindex = ($paletteindexbyte & 0xf << 4 * $i) >> 4 * $i;
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                     $col++;
                                 }
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 8:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $paletteindex = ord($BMPpixelData[$pixeldataoffset++]);
                                 $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 24:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $thisfile_bmp['data'][$row][$col] = ord($BMPpixelData[$pixeldataoffset + 2]) << 16 | ord($BMPpixelData[$pixeldataoffset + 1]) << 8 | ord($BMPpixelData[$pixeldataoffset]);
                                 $pixeldataoffset += 3;
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 32:
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $thisfile_bmp['data'][$row][$col] = ord($BMPpixelData[$pixeldataoffset + 3]) << 24 | ord($BMPpixelData[$pixeldataoffset + 2]) << 16 | ord($BMPpixelData[$pixeldataoffset + 1]) << 8 | ord($BMPpixelData[$pixeldataoffset]);
                                 $pixeldataoffset += 4;
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     case 16:
                         // ?
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             case 1:
                 // BI_RLE8 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 8:
                         $pixelcounter = 0;
                         while ($pixeldataoffset < strlen($BMPpixelData)) {
                             $firstbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             $secondbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             if ($firstbyte == 0) {
                                 // escaped/absolute mode - the first byte of the pair can be set to zero to
                                 // indicate an escape character that denotes the end of a line, the end of
                                 // a bitmap, or a delta, depending on the value of the second byte.
                                 switch ($secondbyte) {
                                     case 0:
                                         // end of line
                                         // no need for special processing, just ignore
                                         break;
                                     case 1:
                                         // end of bitmap
                                         $pixeldataoffset = strlen($BMPpixelData);
                                         // force to exit loop just in case
                                         break;
                                     case 2:
                                         // delta - The 2 bytes following the escape contain unsigned values
                                         // indicating the horizontal and vertical offsets of the next pixel
                                         // from the current position.
                                         $colincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $rowincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $col = $pixelcounter % $thisfile_bmp_header_raw['width'] + $colincrement;
                                         $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'] - $rowincrement;
                                         $pixelcounter = $row * $thisfile_bmp_header_raw['width'] + $col;
                                         break;
                                     default:
                                         // In absolute mode, the first byte is zero and the second byte is a
                                         // value in the range 03H through FFH. The second byte represents the
                                         // number of bytes that follow, each of which contains the color index
                                         // of a single pixel. Each run must be aligned on a word boundary.
                                         for ($i = 0; $i < $secondbyte; $i++) {
                                             $paletteindex = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                             $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                             $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                             $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                             $pixelcounter++;
                                         }
                                         while ($pixeldataoffset % 2 != 0) {
                                             // Each run must be aligned on a word boundary.
                                             $pixeldataoffset++;
                                         }
                                         break;
                                 }
                             } else {
                                 // encoded mode - the first byte specifies the number of consecutive pixels
                                 // to be drawn using the color index contained in the second byte.
                                 for ($i = 0; $i < $firstbyte; $i++) {
                                     $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                     $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$secondbyte];
                                     $pixelcounter++;
                                 }
                             }
                         }
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             case 2:
                 // BI_RLE4 - http://msdn.microsoft.com/library/en-us/gdi/bitmaps_6x0u.asp
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 4:
                         $pixelcounter = 0;
                         while ($pixeldataoffset < strlen($BMPpixelData)) {
                             $firstbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             $secondbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             if ($firstbyte == 0) {
                                 // escaped/absolute mode - the first byte of the pair can be set to zero to
                                 // indicate an escape character that denotes the end of a line, the end of
                                 // a bitmap, or a delta, depending on the value of the second byte.
                                 switch ($secondbyte) {
                                     case 0:
                                         // end of line
                                         // no need for special processing, just ignore
                                         break;
                                     case 1:
                                         // end of bitmap
                                         $pixeldataoffset = strlen($BMPpixelData);
                                         // force to exit loop just in case
                                         break;
                                     case 2:
                                         // delta - The 2 bytes following the escape contain unsigned values
                                         // indicating the horizontal and vertical offsets of the next pixel
                                         // from the current position.
                                         $colincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $rowincrement = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $col = $pixelcounter % $thisfile_bmp_header_raw['width'] + $colincrement;
                                         $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'] - $rowincrement;
                                         $pixelcounter = $row * $thisfile_bmp_header_raw['width'] + $col;
                                         break;
                                     default:
                                         // In absolute mode, the first byte is zero. The second byte contains the number
                                         // of color indexes that follow. Subsequent bytes contain color indexes in their
                                         // high- and low-order 4 bits, one color index for each pixel. In absolute mode,
                                         // each run must be aligned on a word boundary.
                                         unset($paletteindexes);
                                         for ($i = 0; $i < ceil($secondbyte / 2); $i++) {
                                             $paletteindexbyte = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                             $paletteindexes[] = ($paletteindexbyte & 0xf0) >> 4;
                                             $paletteindexes[] = $paletteindexbyte & 0xf;
                                         }
                                         while ($pixeldataoffset % 2 != 0) {
                                             // Each run must be aligned on a word boundary.
                                             $pixeldataoffset++;
                                         }
                                         foreach ($paletteindexes as $paletteindex) {
                                             $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                             $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                             $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindex];
                                             $pixelcounter++;
                                         }
                                         break;
                                 }
                             } else {
                                 // encoded mode - the first byte of the pair contains the number of pixels to be
                                 // drawn using the color indexes in the second byte. The second byte contains two
                                 // color indexes, one in its high-order 4 bits and one in its low-order 4 bits.
                                 // The first of the pixels is drawn using the color specified by the high-order
                                 // 4 bits, the second is drawn using the color in the low-order 4 bits, the third
                                 // is drawn using the color in the high-order 4 bits, and so on, until all the
                                 // pixels specified by the first byte have been drawn.
                                 $paletteindexes[0] = ($secondbyte & 0xf0) >> 4;
                                 $paletteindexes[1] = $secondbyte & 0xf;
                                 for ($i = 0; $i < $firstbyte; $i++) {
                                     $col = $pixelcounter % $thisfile_bmp_header_raw['width'];
                                     $row = $thisfile_bmp_header_raw['height'] - 1 - ($pixelcounter - $col) / $thisfile_bmp_header_raw['width'];
                                     $thisfile_bmp['data'][$row][$col] = $thisfile_bmp['palette'][$paletteindexes[$i % 2]];
                                     $pixelcounter++;
                                 }
                             }
                         }
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             case 3:
                 // BI_BITFIELDS
                 switch ($thisfile_bmp_header_raw['bits_per_pixel']) {
                     case 16:
                     case 32:
                         $redshift = 0;
                         $greenshift = 0;
                         $blueshift = 0;
                         while (($thisfile_bmp_header_raw['red_mask'] >> $redshift & 0x1) == 0) {
                             $redshift++;
                         }
                         while (($thisfile_bmp_header_raw['green_mask'] >> $greenshift & 0x1) == 0) {
                             $greenshift++;
                         }
                         while (($thisfile_bmp_header_raw['blue_mask'] >> $blueshift & 0x1) == 0) {
                             $blueshift++;
                         }
                         for ($row = $thisfile_bmp_header_raw['height'] - 1; $row >= 0; $row--) {
                             for ($col = 0; $col < $thisfile_bmp_header_raw['width']; $col++) {
                                 $pixelvalue = Utils::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset, $thisfile_bmp_header_raw['bits_per_pixel'] / 8));
                                 $pixeldataoffset += $thisfile_bmp_header_raw['bits_per_pixel'] / 8;
                                 $red = intval(round((($pixelvalue & $thisfile_bmp_header_raw['red_mask']) >> $redshift) / ($thisfile_bmp_header_raw['red_mask'] >> $redshift) * 255));
                                 $green = intval(round((($pixelvalue & $thisfile_bmp_header_raw['green_mask']) >> $greenshift) / ($thisfile_bmp_header_raw['green_mask'] >> $greenshift) * 255));
                                 $blue = intval(round((($pixelvalue & $thisfile_bmp_header_raw['blue_mask']) >> $blueshift) / ($thisfile_bmp_header_raw['blue_mask'] >> $blueshift) * 255));
                                 $thisfile_bmp['data'][$row][$col] = $red << 16 | $green << 8 | $blue;
                             }
                             while ($pixeldataoffset % 4 != 0) {
                                 // lines are padded to nearest DWORD
                                 $pixeldataoffset++;
                             }
                         }
                         break;
                     default:
                         $info['error'][] = 'Unknown bits-per-pixel value (' . $thisfile_bmp_header_raw['bits_per_pixel'] . ') - cannot read pixel data';
                         break;
                 }
                 break;
             default:
                 // unhandled compression type
                 $info['error'][] = 'Unknown/unhandled compression type value (' . $thisfile_bmp_header_raw['compression'] . ') - cannot decompress pixel data';
                 break;
         }
     }
     return true;
 }