Exemple #1
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $DSSheader = fread($this->getid3->fp, 1256);
     if (!preg_match('#^(\\x02|\\x03)dss#', $DSSheader)) {
         $info['error'][] = 'Expecting "[02-03] 64 73 73" at offset ' . $info['avdataoffset'] . ', found "' . Helper::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
     // shortcut
     $info['dss'] = array();
     $thisfile_dss =& $info['dss'];
     $info['fileformat'] = 'dss';
     $info['audio']['dataformat'] = 'dss';
     $info['audio']['bitrate_mode'] = 'cbr';
     //$thisfile_dss['encoding']              = 'ISO-8859-1';
     $thisfile_dss['version'] = ord(substr($DSSheader, 0, 1));
     $thisfile_dss['date_create'] = self::DSSdateStringToUnixDate(substr($DSSheader, 38, 12));
     $thisfile_dss['date_complete'] = self::DSSdateStringToUnixDate(substr($DSSheader, 50, 12));
     //$thisfile_dss['length']         =                         intval(substr($DSSheader,  62,   6)); // I thought time was in seconds, it's actually HHMMSS
     $thisfile_dss['length'] = intval(substr($DSSheader, 62, 2) * 3600 + substr($DSSheader, 64, 2) * 60 + substr($DSSheader, 66, 2));
     $thisfile_dss['priority'] = ord(substr($DSSheader, 793, 1));
     $thisfile_dss['comments'] = trim(substr($DSSheader, 798, 100));
     //$info['audio']['bits_per_sample']  = ?;
     //$info['audio']['sample_rate']      = ?;
     $info['audio']['channels'] = 1;
     $info['playtime_seconds'] = $thisfile_dss['length'];
     $info['audio']['bitrate'] = $info['filesize'] * 8 / $info['playtime_seconds'];
     return true;
 }
Exemple #2
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'rar';
     if ($this->option_use_rar_extension === true) {
         if (function_exists('rar_open')) {
             if ($rp = rar_open($info['filenamepath'])) {
                 $info['rar']['files'] = array();
                 $entries = rar_list($rp);
                 foreach ($entries as $entry) {
                     $info['rar']['files'] = Helper::array_merge_clobber($info['rar']['files'], Helper::CreateDeepArray($entry->getName(), '/', $entry->getUnpackedSize()));
                 }
                 rar_close($rp);
                 return true;
             } else {
                 $info['error'][] = 'failed to rar_open(' . $info['filename'] . ')';
             }
         } else {
             $info['error'][] = 'RAR support does not appear to be available in this PHP installation';
         }
     } else {
         $info['error'][] = 'PHP-RAR processing has been disabled (set $getid3_rar->option_use_rar_extension=true to enable)';
     }
     return false;
 }
Exemple #3
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $AAheader = fread($this->getid3->fp, 8);
     $magic = "W�u6";
     if (substr($AAheader, 4, 4) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($AAheader, 4, 4)) . '"';
         return false;
     }
     // shortcut
     $info['aa'] = array();
     $thisfile_au =& $info['aa'];
     $info['fileformat'] = 'aa';
     $info['audio']['dataformat'] = 'aa';
     $info['error'][] = 'Audible Audiobook (.aa) parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']';
     return false;
     $info['audio']['bitrate_mode'] = 'cbr';
     // is it?
     $thisfile_au['encoding'] = 'ISO-8859-1';
     $thisfile_au['filesize'] = Helper::BigEndian2Int(substr($AUheader, 0, 4));
     if ($thisfile_au['filesize'] > $info['avdataend'] - $info['avdataoffset']) {
         $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['filesize'] . '" bytes of data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"';
     }
     $info['audio']['bits_per_sample'] = 16;
     // is it?
     $info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
     $info['audio']['channels'] = $thisfile_au['channels'];
     //$info['playtime_seconds'] = 0;
     //$info['audio']['bitrate'] = 0;
     return true;
 }
Exemple #4
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $EXEheader = fread($this->getid3->fp, 28);
     $magic = 'MZ';
     if (substr($EXEheader, 0, 2) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($EXEheader, 0, 2)) . '"';
         return false;
     }
     $info['fileformat'] = 'exe';
     $info['exe']['mz']['magic'] = 'MZ';
     $info['exe']['mz']['raw']['last_page_size'] = Helper::LittleEndian2Int(substr($EXEheader, 2, 2));
     $info['exe']['mz']['raw']['page_count'] = Helper::LittleEndian2Int(substr($EXEheader, 4, 2));
     $info['exe']['mz']['raw']['relocation_count'] = Helper::LittleEndian2Int(substr($EXEheader, 6, 2));
     $info['exe']['mz']['raw']['header_paragraphs'] = Helper::LittleEndian2Int(substr($EXEheader, 8, 2));
     $info['exe']['mz']['raw']['min_memory_paragraphs'] = Helper::LittleEndian2Int(substr($EXEheader, 10, 2));
     $info['exe']['mz']['raw']['max_memory_paragraphs'] = Helper::LittleEndian2Int(substr($EXEheader, 12, 2));
     $info['exe']['mz']['raw']['initial_ss'] = Helper::LittleEndian2Int(substr($EXEheader, 14, 2));
     $info['exe']['mz']['raw']['initial_sp'] = Helper::LittleEndian2Int(substr($EXEheader, 16, 2));
     $info['exe']['mz']['raw']['checksum'] = Helper::LittleEndian2Int(substr($EXEheader, 18, 2));
     $info['exe']['mz']['raw']['cs_ip'] = Helper::LittleEndian2Int(substr($EXEheader, 20, 4));
     $info['exe']['mz']['raw']['relocation_table_offset'] = Helper::LittleEndian2Int(substr($EXEheader, 24, 2));
     $info['exe']['mz']['raw']['overlay_number'] = Helper::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 GetId3Core() [' . $this->getid3->version() . ']';
     return false;
 }
Exemple #5
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $SZIPHeader = fread($this->getid3->fp, 6);
     if (substr($SZIPHeader, 0, 4) != "SZ\n") {
         $info['error'][] = 'Expecting "53 5A 0A 04" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($SZIPHeader, 0, 4)) . '"';
         return false;
     }
     $info['fileformat'] = 'szip';
     $info['szip']['major_version'] = Helper::BigEndian2Int(substr($SZIPHeader, 4, 1));
     $info['szip']['minor_version'] = Helper::BigEndian2Int(substr($SZIPHeader, 5, 1));
     while (!feof($this->getid3->fp)) {
         $NextBlockID = fread($this->getid3->fp, 2);
         switch ($NextBlockID) {
             case 'SZ':
                 // Note that szip files can be concatenated, this has the same effect as
                 // concatenating the files. this also means that global header blocks
                 // might be present between directory/data blocks.
                 fseek($this->getid3->fp, 4, SEEK_CUR);
                 break;
             case 'BH':
                 $BHheaderbytes = Helper::BigEndian2Int(fread($this->getid3->fp, 3));
                 $BHheaderdata = fread($this->getid3->fp, $BHheaderbytes);
                 $BHheaderoffset = 0;
                 while (strpos($BHheaderdata, "", $BHheaderoffset) > 0) {
                     //filename as \0 terminated string  (empty string indicates end)
                     //owner as \0 terminated string (empty is same as last file)
                     //group as \0 terminated string (empty is same as last file)
                     //3 byte filelength in this block
                     //2 byte access flags
                     //4 byte creation time (like in unix)
                     //4 byte modification time (like in unix)
                     //4 byte access time (like in unix)
                     $BHdataArray['filename'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['filename']) + 1;
                     $BHdataArray['owner'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['owner']) + 1;
                     $BHdataArray['group'] = substr($BHheaderdata, $BHheaderoffset, strcspn($BHheaderdata, ""));
                     $BHheaderoffset += strlen($BHdataArray['group']) + 1;
                     $BHdataArray['filelength'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 3));
                     $BHheaderoffset += 3;
                     $BHdataArray['access_flags'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 2));
                     $BHheaderoffset += 2;
                     $BHdataArray['creation_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $BHdataArray['modification_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $BHdataArray['access_time'] = Helper::BigEndian2Int(substr($BHheaderdata, $BHheaderoffset, 4));
                     $BHheaderoffset += 4;
                     $info['szip']['BH'][] = $BHdataArray;
                 }
                 break;
             default:
                 break 2;
         }
     }
     return true;
 }
Exemple #6
0
 /**
  * @return bool
  */
 public function ParseBink()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'bink';
     $info['video']['dataformat'] = 'bink';
     $fileData = 'BIK' . fread($this->getid3->fp, 13);
     $info['bink']['data_size'] = Helper::LittleEndian2Int(substr($fileData, 4, 4));
     $info['bink']['frame_count'] = Helper::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;
 }
Exemple #7
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $DOCFILEheader = fread($this->getid3->fp, 8);
     $magic = "поЮ║╠А";
     if (substr($DOCFILEheader, 0, 8) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at ' . $info['avdataoffset'] . ', found ' . Helper::PrintHexBytes(substr($DOCFILEheader, 0, 8)) . ' instead.';
         return false;
     }
     $info['fileformat'] = 'msoffice';
     $info['error'][] = 'MS Office (.doc, .xls, etc) parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']';
     return false;
 }
Exemple #8
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $RKAUHeader = fread($this->getid3->fp, 20);
     $magic = 'RKA';
     if (substr($RKAUHeader, 0, 3) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($RKAUHeader, 0, 3)) . '"';
         return false;
     }
     $info['fileformat'] = 'rkau';
     $info['audio']['dataformat'] = 'rkau';
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['rkau']['raw']['version'] = Helper::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 GetId3Core() [' . $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'] = Helper::LittleEndian2Int(substr($RKAUHeader, 4, 4));
     $info['rkau']['sample_rate'] = Helper::LittleEndian2Int(substr($RKAUHeader, 8, 4));
     $info['rkau']['channels'] = Helper::LittleEndian2Int(substr($RKAUHeader, 12, 1));
     $info['rkau']['bits_per_sample'] = Helper::LittleEndian2Int(substr($RKAUHeader, 13, 1));
     $info['rkau']['raw']['quality'] = Helper::LittleEndian2Int(substr($RKAUHeader, 14, 1));
     $this->RKAUqualityLookup($info['rkau']);
     $info['rkau']['raw']['flags'] = Helper::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'] = Helper::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;
 }
Exemple #9
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $AUheader = fread($this->getid3->fp, 8);
     $magic = '.snd';
     if (substr($AUheader, 0, 4) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" (".snd") at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes(substr($AUheader, 0, 4)) . '"';
         return false;
     }
     // shortcut
     $info['au'] = array();
     $thisfile_au =& $info['au'];
     $info['fileformat'] = 'au';
     $info['audio']['dataformat'] = 'au';
     $info['audio']['bitrate_mode'] = 'cbr';
     $thisfile_au['encoding'] = 'ISO-8859-1';
     $thisfile_au['header_length'] = Helper::BigEndian2Int(substr($AUheader, 4, 4));
     $AUheader .= fread($this->getid3->fp, $thisfile_au['header_length'] - 8);
     $info['avdataoffset'] += $thisfile_au['header_length'];
     $thisfile_au['data_size'] = Helper::BigEndian2Int(substr($AUheader, 8, 4));
     $thisfile_au['data_format_id'] = Helper::BigEndian2Int(substr($AUheader, 12, 4));
     $thisfile_au['sample_rate'] = Helper::BigEndian2Int(substr($AUheader, 16, 4));
     $thisfile_au['channels'] = Helper::BigEndian2Int(substr($AUheader, 20, 4));
     $thisfile_au['comments']['comment'][] = trim(substr($AUheader, 24));
     $thisfile_au['data_format'] = $this->AUdataFormatNameLookup($thisfile_au['data_format_id']);
     $thisfile_au['used_bits_per_sample'] = $this->AUdataFormatUsedBitsPerSampleLookup($thisfile_au['data_format_id']);
     if ($thisfile_au['bits_per_sample'] = $this->AUdataFormatBitsPerSampleLookup($thisfile_au['data_format_id'])) {
         $info['audio']['bits_per_sample'] = $thisfile_au['bits_per_sample'];
     } else {
         unset($thisfile_au['bits_per_sample']);
     }
     $info['audio']['sample_rate'] = $thisfile_au['sample_rate'];
     $info['audio']['channels'] = $thisfile_au['channels'];
     if ($info['avdataoffset'] + $thisfile_au['data_size'] > $info['avdataend']) {
         $info['warning'][] = 'Possible truncated file - expecting "' . $thisfile_au['data_size'] . '" bytes of audio data, only found ' . ($info['avdataend'] - $info['avdataoffset']) . ' bytes"';
     }
     $info['playtime_seconds'] = $thisfile_au['data_size'] / ($thisfile_au['sample_rate'] * $thisfile_au['channels'] * ($thisfile_au['used_bits_per_sample'] / 8));
     $info['audio']['bitrate'] = $thisfile_au['data_size'] * 8 / $info['playtime_seconds'];
     return true;
 }
Exemple #10
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $efaxheader = fread($this->getid3->fp, 1024);
     $info['efax']['header']['magic'] = substr($efaxheader, 0, 2);
     if ($info['efax']['header']['magic'] != "Üþ") {
         $info['error'][] = 'Invalid eFax byte order identifier (expecting DC FE, found ' . Helper::PrintHexBytes($info['efax']['header']['magic']) . ') at offset ' . $info['avdataoffset'];
         return false;
     }
     $info['fileformat'] = 'efax';
     $info['efax']['header']['filesize'] = Helper::LittleEndian2Int(substr($efaxheader, 2, 4));
     if ($info['efax']['header']['filesize'] != $info['filesize']) {
         $info['error'][] = 'Probable ' . ($info['efax']['header']['filesize'] > $info['filesize'] ? 'truncated' : 'corrupt') . ' file, expecting ' . $info['efax']['header']['filesize'] . ' bytes, found ' . $info['filesize'] . ' bytes';
     }
     $info['efax']['header']['software1'] = rtrim(substr($efaxheader, 26, 32), "");
     $info['efax']['header']['software2'] = rtrim(substr($efaxheader, 58, 32), "");
     $info['efax']['header']['software3'] = rtrim(substr($efaxheader, 90, 32), "");
     $info['efax']['header']['pages'] = Helper::LittleEndian2Int(substr($efaxheader, 198, 2));
     $info['efax']['header']['data_bytes'] = Helper::LittleEndian2Int(substr($efaxheader, 202, 4));
     $info['error'][] = 'eFax parsing not enabled in this version of GetId3Core() [' . $this->getid3->version() . ']';
     return false;
     return true;
 }
 /**
  * @return type
  */
 public function getBit()
 {
     $result = Helper::BigEndian2Int(substr($this->sps, $this->currentBytes, 1)) >> 7 - $this->currentBits & 0x1;
     $this->skipBits(1);
     return $result;
 }
Exemple #12
0
 /**
  * @return bool
  */
 public function CalculateReplayGain()
 {
     if (isset($this->info['replay_gain'])) {
         if (!isset($this->info['replay_gain']['reference_volume'])) {
             $this->info['replay_gain']['reference_volume'] = (double) 89.0;
         }
         if (isset($this->info['replay_gain']['track']['adjustment'])) {
             $this->info['replay_gain']['track']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['track']['adjustment'];
         }
         if (isset($this->info['replay_gain']['album']['adjustment'])) {
             $this->info['replay_gain']['album']['volume'] = $this->info['replay_gain']['reference_volume'] - $this->info['replay_gain']['album']['adjustment'];
         }
         if (isset($this->info['replay_gain']['track']['peak'])) {
             $this->info['replay_gain']['track']['max_noclip_gain'] = 0 - Helper::RGADamplitude2dB($this->info['replay_gain']['track']['peak']);
         }
         if (isset($this->info['replay_gain']['album']['peak'])) {
             $this->info['replay_gain']['album']['max_noclip_gain'] = 0 - Helper::RGADamplitude2dB($this->info['replay_gain']['album']['peak']);
         }
     }
     return true;
 }
Exemple #13
0
 /**
  * @return bool
  */
 public function ParseOptimFROGheader45()
 {
     // for fileformat of v4.50a and higher
     $info =& $this->getid3->info;
     $RIFFdata = '';
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     while (!feof($this->getid3->fp) && ftell($this->getid3->fp) < $info['avdataend']) {
         $BlockOffset = ftell($this->getid3->fp);
         $BlockData = fread($this->getid3->fp, 8);
         $offset = 8;
         $BlockName = substr($BlockData, 0, 4);
         $BlockSize = Helper::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 .= fread($this->getid3->fp, $BlockSize);
                 $thisfile_ofr_thisblock['total_samples'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 6));
                 $offset += 6;
                 $thisfile_ofr_thisblock['raw']['sample_type'] = Helper::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'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $thisfile_ofr_thisblock['channels'] = $thisfile_ofr_thisblock['channel_config'];
                 $offset += 1;
                 $thisfile_ofr_thisblock['sample_rate'] = Helper::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'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 2));
                     $thisfile_ofr_thisblock['encoder'] = $this->OptimFROGencoderNameLookup($thisfile_ofr_thisblock['raw']['encoder_id']);
                     $offset += 2;
                     $thisfile_ofr_thisblock['raw']['compression'] = Helper::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(Helper::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 .= fread($this->getid3->fp, 14);
                 fseek($this->getid3->fp, $BlockSize - 14, SEEK_CUR);
                 $COMPdata['crc_32'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['sample_count'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 4));
                 $offset += 4;
                 $COMPdata['raw']['sample_type'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['sample_type'] = $this->OptimFROGsampleTypeLookup($COMPdata['raw']['sample_type']);
                 $offset += 1;
                 $COMPdata['raw']['channel_configuration'] = Helper::LittleEndian2Int(substr($BlockData, $offset, 1));
                 $COMPdata['channel_configuration'] = $this->OptimFROGchannelConfigurationLookup($COMPdata['raw']['channel_configuration']);
                 $offset += 1;
                 $COMPdata['raw']['algorithm_id'] = Helper::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'] = Helper::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 .= fread($this->getid3->fp, $BlockSize);
                 break;
             case 'TAIL':
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 if ($BlockSize > 0) {
                     $RIFFdata .= fread($this->getid3->fp, $BlockSize);
                 }
                 break;
             case 'RECV':
                 // block contains no useful meta data - simply note and skip
                 $thisfile_ofr_thisblock['offset'] = $BlockOffset;
                 $thisfile_ofr_thisblock['size'] = $BlockSize;
                 fseek($this->getid3->fp, $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 GetId3Core()';
                 fseek($this->getid3->fp, $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'] = fread($this->getid3->fp, $BlockSize);
                     $thisfile_ofr_thisblock['md5_string'] = Helper::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';
                     fseek($this->getid3->fp, $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'];
                 fseek($this->getid3->fp, $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 GetId3Core();
     $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;
 }
Exemple #14
0
 /**
  * @param  type    $Header4Bytes
  *
  * @return bool
  */
 public static function MPEGaudioHeaderDecode($Header4Bytes)
 {
     // AAAA AAAA  AAAB BCCD  EEEE FFGH  IIJJ KLMM
     // A - Frame sync (all bits set)
     // B - MPEG Audio version ID
     // C - Layer description
     // D - Protection bit
     // E - Bitrate index
     // F - Sampling rate frequency index
     // G - Padding bit
     // H - Private bit
     // I - Channel Mode
     // J - Mode extension (Only if Joint stereo)
     // K - Copyright
     // L - Original
     // M - Emphasis
     if (strlen($Header4Bytes) != 4) {
         return false;
     }
     $MPEGrawHeader['synch'] = (Helper::BigEndian2Int(substr($Header4Bytes, 0, 2)) & 0xffe0) >> 4;
     $MPEGrawHeader['version'] = (ord($Header4Bytes[1]) & 0x18) >> 3;
     //    BB
     $MPEGrawHeader['layer'] = (ord($Header4Bytes[1]) & 0x6) >> 1;
     //      CC
     $MPEGrawHeader['protection'] = ord($Header4Bytes[1]) & 0x1;
     //        D
     $MPEGrawHeader['bitrate'] = (ord($Header4Bytes[2]) & 0xf0) >> 4;
     // EEEE
     $MPEGrawHeader['sample_rate'] = (ord($Header4Bytes[2]) & 0xc) >> 2;
     //     FF
     $MPEGrawHeader['padding'] = (ord($Header4Bytes[2]) & 0x2) >> 1;
     //       G
     $MPEGrawHeader['private'] = ord($Header4Bytes[2]) & 0x1;
     //        H
     $MPEGrawHeader['channelmode'] = (ord($Header4Bytes[3]) & 0xc0) >> 6;
     // II
     $MPEGrawHeader['modeextension'] = (ord($Header4Bytes[3]) & 0x30) >> 4;
     //   JJ
     $MPEGrawHeader['copyright'] = (ord($Header4Bytes[3]) & 0x8) >> 3;
     //     K
     $MPEGrawHeader['original'] = (ord($Header4Bytes[3]) & 0x4) >> 2;
     //      L
     $MPEGrawHeader['emphasis'] = ord($Header4Bytes[3]) & 0x3;
     //       MM
     return $MPEGrawHeader;
 }
Exemple #15
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $FLVdataLength = $info['avdataend'] - $info['avdataoffset'];
     $FLVheader = fread($this->getid3->fp, 5);
     $info['fileformat'] = 'flv';
     $info['flv']['header']['signature'] = substr($FLVheader, 0, 3);
     $info['flv']['header']['version'] = Helper::BigEndian2Int(substr($FLVheader, 3, 1));
     $TypeFlags = Helper::BigEndian2Int(substr($FLVheader, 4, 1));
     $magic = 'FLV';
     if ($info['flv']['header']['signature'] != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($info['flv']['header']['signature']) . '"';
         unset($info['flv']);
         unset($info['fileformat']);
         return false;
     }
     $info['flv']['header']['hasAudio'] = (bool) ($TypeFlags & 0x4);
     $info['flv']['header']['hasVideo'] = (bool) ($TypeFlags & 0x1);
     $FrameSizeDataLength = Helper::BigEndian2Int(fread($this->getid3->fp, 4));
     $FLVheaderFrameLength = 9;
     if ($FrameSizeDataLength > $FLVheaderFrameLength) {
         fseek($this->getid3->fp, $FrameSizeDataLength - $FLVheaderFrameLength, SEEK_CUR);
     }
     $Duration = 0;
     $found_video = false;
     $found_audio = false;
     $found_meta = false;
     $found_valid_meta_playtime = false;
     $tagParseCount = 0;
     $info['flv']['framecount'] = array('total' => 0, 'audio' => 0, 'video' => 0);
     $flv_framecount =& $info['flv']['framecount'];
     while (ftell($this->getid3->fp) + 16 < $info['avdataend'] && ($tagParseCount++ <= $this->max_frames || !$found_valid_meta_playtime)) {
         $ThisTagHeader = fread($this->getid3->fp, 16);
         $PreviousTagLength = Helper::BigEndian2Int(substr($ThisTagHeader, 0, 4));
         $TagType = Helper::BigEndian2Int(substr($ThisTagHeader, 4, 1));
         $DataLength = Helper::BigEndian2Int(substr($ThisTagHeader, 5, 3));
         $Timestamp = Helper::BigEndian2Int(substr($ThisTagHeader, 8, 3));
         $LastHeaderByte = Helper::BigEndian2Int(substr($ThisTagHeader, 15, 1));
         $NextOffset = ftell($this->getid3->fp) - 1 + $DataLength;
         if ($Timestamp > $Duration) {
             $Duration = $Timestamp;
         }
         ++$flv_framecount['total'];
         switch ($TagType) {
             case self::GETID3_FLV_TAG_AUDIO:
                 $flv_framecount['audio']++;
                 if (!$found_audio) {
                     $found_audio = true;
                     $info['flv']['audio']['audioFormat'] = $LastHeaderByte >> 4 & 0xf;
                     $info['flv']['audio']['audioRate'] = $LastHeaderByte >> 2 & 0x3;
                     $info['flv']['audio']['audioSampleSize'] = $LastHeaderByte >> 1 & 0x1;
                     $info['flv']['audio']['audioType'] = $LastHeaderByte & 0x1;
                 }
                 break;
             case self::GETID3_FLV_TAG_VIDEO:
                 $flv_framecount['video']++;
                 if (!$found_video) {
                     $found_video = true;
                     $info['flv']['video']['videoCodec'] = $LastHeaderByte & 0x7;
                     $FLVvideoHeader = fread($this->getid3->fp, 11);
                     if ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H264) {
                         // this code block contributed by: moysevichØgmail*com
                         $AVCPacketType = Helper::BigEndian2Int(substr($FLVvideoHeader, 0, 1));
                         if ($AVCPacketType == AVCSequenceParameterSetReader::H264_AVC_SEQUENCE_HEADER) {
                             //	read AVCDecoderConfigurationRecord
                             $configurationVersion = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 1));
                             $AVCProfileIndication = Helper::BigEndian2Int(substr($FLVvideoHeader, 5, 1));
                             $profile_compatibility = Helper::BigEndian2Int(substr($FLVvideoHeader, 6, 1));
                             $lengthSizeMinusOne = Helper::BigEndian2Int(substr($FLVvideoHeader, 7, 1));
                             $numOfSequenceParameterSets = Helper::BigEndian2Int(substr($FLVvideoHeader, 8, 1));
                             if (($numOfSequenceParameterSets & 0x1f) != 0) {
                                 //	there is at least one SequenceParameterSet
                                 //	read size of the first SequenceParameterSet
                                 //$spsSize = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 9, 2));
                                 $spsSize = Helper::LittleEndian2Int(substr($FLVvideoHeader, 9, 2));
                                 //	read the first SequenceParameterSet
                                 $sps = fread($this->getid3->fp, $spsSize);
                                 if (strlen($sps) == $spsSize) {
                                     //	make sure that whole SequenceParameterSet was red
                                     $spsReader = new AVCSequenceParameterSetReader($sps);
                                     $spsReader->readData();
                                     $info['video']['resolution_x'] = $spsReader->getWidth();
                                     $info['video']['resolution_y'] = $spsReader->getHeight();
                                 }
                             }
                         }
                         // end: moysevichØgmail*com
                     } elseif ($info['flv']['video']['videoCodec'] == self::GETID3_FLV_VIDEO_H263) {
                         $PictureSizeType = Helper::BigEndian2Int(substr($FLVvideoHeader, 3, 2)) >> 7;
                         $PictureSizeType = $PictureSizeType & 0x7;
                         $info['flv']['header']['videoSizeType'] = $PictureSizeType;
                         switch ($PictureSizeType) {
                             case 0:
                                 //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
                                 //$PictureSizeEnc <<= 1;
                                 //$info['video']['resolution_x'] = ($PictureSizeEnc & 0xFF00) >> 8;
                                 //$PictureSizeEnc = GetId3_lib::BigEndian2Int(substr($FLVvideoHeader, 6, 2));
                                 //$PictureSizeEnc <<= 1;
                                 //$info['video']['resolution_y'] = ($PictureSizeEnc & 0xFF00) >> 8;
                                 $PictureSizeEnc['x'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 2));
                                 $PictureSizeEnc['y'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 5, 2));
                                 $PictureSizeEnc['x'] >>= 7;
                                 $PictureSizeEnc['y'] >>= 7;
                                 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xff;
                                 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xff;
                                 break;
                             case 1:
                                 $PictureSizeEnc['x'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 4, 3));
                                 $PictureSizeEnc['y'] = Helper::BigEndian2Int(substr($FLVvideoHeader, 6, 3));
                                 $PictureSizeEnc['x'] >>= 7;
                                 $PictureSizeEnc['y'] >>= 7;
                                 $info['video']['resolution_x'] = $PictureSizeEnc['x'] & 0xffff;
                                 $info['video']['resolution_y'] = $PictureSizeEnc['y'] & 0xffff;
                                 break;
                             case 2:
                                 $info['video']['resolution_x'] = 352;
                                 $info['video']['resolution_y'] = 288;
                                 break;
                             case 3:
                                 $info['video']['resolution_x'] = 176;
                                 $info['video']['resolution_y'] = 144;
                                 break;
                             case 4:
                                 $info['video']['resolution_x'] = 128;
                                 $info['video']['resolution_y'] = 96;
                                 break;
                             case 5:
                                 $info['video']['resolution_x'] = 320;
                                 $info['video']['resolution_y'] = 240;
                                 break;
                             case 6:
                                 $info['video']['resolution_x'] = 160;
                                 $info['video']['resolution_y'] = 120;
                                 break;
                             default:
                                 $info['video']['resolution_x'] = 0;
                                 $info['video']['resolution_y'] = 0;
                                 break;
                         }
                     }
                     $info['video']['pixel_aspect_ratio'] = $info['video']['resolution_x'] / $info['video']['resolution_y'];
                 }
                 break;
                 // Meta tag
             // Meta tag
             case self::GETID3_FLV_TAG_META:
                 if (!$found_meta) {
                     $found_meta = true;
                     fseek($this->getid3->fp, -1, SEEK_CUR);
                     $datachunk = fread($this->getid3->fp, $DataLength);
                     $AMFstream = new AMFStream($datachunk);
                     $reader = new AMFReader($AMFstream);
                     $eventName = $reader->readData();
                     $info['flv']['meta'][$eventName] = $reader->readData();
                     unset($reader);
                     $copykeys = array('framerate' => 'frame_rate', 'width' => 'resolution_x', 'height' => 'resolution_y', 'audiodatarate' => 'bitrate', 'videodatarate' => 'bitrate');
                     foreach ($copykeys as $sourcekey => $destkey) {
                         if (isset($info['flv']['meta']['onMetaData'][$sourcekey])) {
                             switch ($sourcekey) {
                                 case 'width':
                                 case 'height':
                                     $info['video'][$destkey] = intval(round($info['flv']['meta']['onMetaData'][$sourcekey]));
                                     break;
                                 case 'audiodatarate':
                                     $info['audio'][$destkey] = Helper::CastAsInt(round($info['flv']['meta']['onMetaData'][$sourcekey] * 1000));
                                     break;
                                 case 'videodatarate':
                                 case 'frame_rate':
                                 default:
                                     $info['video'][$destkey] = $info['flv']['meta']['onMetaData'][$sourcekey];
                                     break;
                             }
                         }
                     }
                     if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
                         $found_valid_meta_playtime = true;
                     }
                 }
                 break;
             default:
                 // noop
                 break;
         }
         fseek($this->getid3->fp, $NextOffset, SEEK_SET);
     }
     $info['playtime_seconds'] = $Duration / 1000;
     if ($info['playtime_seconds'] > 0) {
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if ($info['flv']['header']['hasAudio']) {
         $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['audio']['audioFormat']);
         $info['audio']['sample_rate'] = $this->FLVaudioRate($info['flv']['audio']['audioRate']);
         $info['audio']['bits_per_sample'] = $this->FLVaudioBitDepth($info['flv']['audio']['audioSampleSize']);
         $info['audio']['channels'] = $info['flv']['audio']['audioType'] + 1;
         // 0=mono,1=stereo
         $info['audio']['lossless'] = $info['flv']['audio']['audioFormat'] ? false : true;
         // 0=uncompressed
         $info['audio']['dataformat'] = 'flv';
     }
     if (!empty($info['flv']['header']['hasVideo'])) {
         $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['video']['videoCodec']);
         $info['video']['dataformat'] = 'flv';
         $info['video']['lossless'] = false;
     }
     // Set information from meta
     if (!empty($info['flv']['meta']['onMetaData']['duration'])) {
         $info['playtime_seconds'] = $info['flv']['meta']['onMetaData']['duration'];
         $info['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     }
     if (isset($info['flv']['meta']['onMetaData']['audiocodecid'])) {
         $info['audio']['codec'] = $this->FLVaudioFormat($info['flv']['meta']['onMetaData']['audiocodecid']);
     }
     if (isset($info['flv']['meta']['onMetaData']['videocodecid'])) {
         $info['video']['codec'] = $this->FLVvideoCodec($info['flv']['meta']['onMetaData']['videocodecid']);
     }
     return true;
 }
Exemple #16
0
 /**
  * @return string
  */
 public function GenerateCONTchunk()
 {
     foreach ($this->tag_data as $key => $value) {
         // limit each value to 0xFFFF bytes
         $this->tag_data[$key] = substr($value, 0, 65535);
     }
     $CONTchunk = "";
     // object version
     $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : 0, 2);
     $CONTchunk .= !empty($this->tag_data['title']) ? strlen($this->tag_data['title']) : '';
     $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : 0, 2);
     $CONTchunk .= !empty($this->tag_data['artist']) ? strlen($this->tag_data['artist']) : '';
     $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : 0, 2);
     $CONTchunk .= !empty($this->tag_data['copyright']) ? strlen($this->tag_data['copyright']) : '';
     $CONTchunk .= Helper::BigEndian2String(!empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : 0, 2);
     $CONTchunk .= !empty($this->tag_data['comment']) ? strlen($this->tag_data['comment']) : '';
     if ($this->paddedlength > strlen($CONTchunk) + 8) {
         $CONTchunk .= str_repeat("", $this->paddedlength - strlen($CONTchunk) - 8);
     }
     $CONTchunk = 'CONT' . Helper::BigEndian2String(strlen($CONTchunk) + 8, 4) . $CONTchunk;
     // CONT chunk identifier + chunk length
     return $CONTchunk;
 }
Exemple #17
0
 /**
  * @param type $BonkTagName
  */
 public function HandleBonkTags($BonkTagName)
 {
     $info =& $this->getid3->info;
     switch ($BonkTagName) {
         case 'BONK':
             // shortcut
             $thisfile_bonk_BONK =& $info['bonk']['BONK'];
             $BonkData = "" . 'BONK' . fread($this->getid3->fp, 17);
             $thisfile_bonk_BONK['version'] = Helper::LittleEndian2Int(substr($BonkData, 5, 1));
             $thisfile_bonk_BONK['number_samples'] = Helper::LittleEndian2Int(substr($BonkData, 6, 4));
             $thisfile_bonk_BONK['sample_rate'] = Helper::LittleEndian2Int(substr($BonkData, 10, 4));
             $thisfile_bonk_BONK['channels'] = Helper::LittleEndian2Int(substr($BonkData, 14, 1));
             $thisfile_bonk_BONK['lossless'] = (bool) Helper::LittleEndian2Int(substr($BonkData, 15, 1));
             $thisfile_bonk_BONK['joint_stereo'] = (bool) Helper::LittleEndian2Int(substr($BonkData, 16, 1));
             $thisfile_bonk_BONK['number_taps'] = Helper::LittleEndian2Int(substr($BonkData, 17, 2));
             $thisfile_bonk_BONK['downsampling_ratio'] = Helper::LittleEndian2Int(substr($BonkData, 19, 1));
             $thisfile_bonk_BONK['samples_per_packet'] = Helper::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'] = Helper::LittleEndian2Int(fread($this->getid3->fp, 1));
             $thisfile_bonk_INFO['entries_count'] = 0;
             $NextInfoDataPair = fread($this->getid3->fp, 5);
             if (!$this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
                 while (!feof($this->getid3->fp)) {
                     //$CurrentSeekInfo['offset']  = GetId3_lib::LittleEndian2Int(substr($NextInfoDataPair, 0, 4));
                     //$CurrentSeekInfo['nextbit'] = GetId3_lib::LittleEndian2Int(substr($NextInfoDataPair, 4, 1));
                     //$thisfile_bonk_INFO[] = $CurrentSeekInfo;
                     $NextInfoDataPair = fread($this->getid3->fp, 5);
                     if ($this->BonkIsValidTagName(substr($NextInfoDataPair, 1, 4))) {
                         fseek($this->getid3->fp, -5, SEEK_CUR);
                         break;
                     }
                     ++$thisfile_bonk_INFO['entries_count'];
                 }
             }
             break;
         case 'META':
             $BonkData = "" . 'META' . fread($this->getid3->fp, $info['bonk']['META']['size'] - 5);
             $info['bonk']['META']['version'] = Helper::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 = Helper::LittleEndian2Int(substr($BonkData, $offset, 4));
                 $offset += 4;
                 $info['bonk']['META']['tags'][$MetaEntryTagName] = $MetaEntryTagOffset;
             }
             break;
         case ' ID3':
             $info['audio']['encoder'] = 'Extended BONK v0.9+';
             // ID3v2 checking is optional
             if (class_exists('Helpers\\GetId3\\Module\\Tag\\Id3v2')) {
                 $getid3_temp = new GetId3Core();
                 $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;
     }
 }
Exemple #18
0
 /**
  * @param  type $items
  * @param  type $isheader
  *
  * @return type
  */
 public function GenerateAPEtagHeaderFooter(&$items, $isheader = false)
 {
     $tagdatalength = 0;
     foreach ($items as $itemdata) {
         $tagdatalength += strlen($itemdata);
     }
     $APEheader = 'APETAGEX';
     $APEheader .= Helper::LittleEndian2String(2000, 4);
     $APEheader .= Helper::LittleEndian2String(32 + $tagdatalength, 4);
     $APEheader .= Helper::LittleEndian2String(count($items), 4);
     $APEheader .= $this->GenerateAPEtagFlags(true, true, $isheader, 0, false);
     $APEheader .= str_repeat("", 8);
     return $APEheader;
 }
Exemple #19
0
 public function RemoveID3v1()
 {
     // File MUST be writeable - CHMOD(646) at least
     if (!empty($this->filename) && is_readable($this->filename) && is_writable($this->filename) && is_file($this->filename)) {
         $this->setRealFileSize();
         if ($this->filesize <= 0 || !Helper::intValueSupported($this->filesize)) {
             $this->errors[] = 'Unable to RemoveID3v1(' . $this->filename . ') because filesize (' . $this->filesize . ') is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
             return false;
         }
         if ($fp_source = fopen($this->filename, 'r+b')) {
             fseek($fp_source, -128, SEEK_END);
             if (fread($fp_source, 3) == 'TAG') {
                 ftruncate($fp_source, $this->filesize - 128);
             } else {
                 // no ID3v1 tag to begin with - do nothing
             }
             fclose($fp_source);
             return true;
         } else {
             $this->errors[] = 'Could not fopen(' . $this->filename . ', "r+b")';
         }
     } else {
         $this->errors[] = $this->filename . ' is not writeable';
     }
     return false;
 }
Exemple #20
0
 /**
  * @return bool
  */
 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;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $GIFheader = fread($this->getid3->fp, 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::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'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 2));
     $offset += 2;
     $info['gif']['header']['raw']['height'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 2));
     $offset += 2;
     $info['gif']['header']['raw']['flags'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 1));
     $offset += 1;
     $info['gif']['header']['raw']['bg_color_index'] = Helper::LittleEndian2Int(substr($GIFheader, $offset, 1));
     $offset += 1;
     $info['gif']['header']['raw']['aspect_ratio'] = Helper::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 = fread($this->getid3->fp, 3 * $info['gif']['header']['global_color_size']);
     //			$offset = 0;
     //			for ($i = 0; $i < $info['gif']['header']['global_color_size']; $i++) {
     //				$red   = GetId3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
     //				$green = GetId3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
     //				$blue  = GetId3_lib::LittleEndian2Int(substr($GIFcolorTable, $offset++, 1));
     //				$info['gif']['global_color_table'][$i] = (($red << 16) | ($green << 8) | ($blue));
     //			}
     //		}
     //
     //		// Image Descriptor
     //		while (!feof($this->getid3->fp)) {
     //			$NextBlockTest = fread($this->getid3->fp, 1);
     //			switch ($NextBlockTest) {
     //
     //				case ',': // ',' - Image separator character
     //
     //					$ImageDescriptorData = $NextBlockTest.fread($this->getid3->fp, 9);
     //					$ImageDescriptor = array();
     //					$ImageDescriptor['image_left']   = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 1, 2));
     //					$ImageDescriptor['image_top']    = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 3, 2));
     //					$ImageDescriptor['image_width']  = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 5, 2));
     //					$ImageDescriptor['image_height'] = GetId3_lib::LittleEndian2Int(substr($ImageDescriptorData, 7, 2));
     //					$ImageDescriptor['flags_raw']    = GetId3_lib::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 GetId3Core() cannot parse local color maps for GIFs';
     //						return true;
     //
     //					}
     //echo 'Start of raster data: '.ftell($this->getid3->fp).'<BR>';
     //					$RasterData = array();
     //					$RasterData['code_size']        = GetId3_lib::LittleEndian2Int(fread($this->getid3->fp, 1));
     //					$RasterData['block_byte_count'] = GetId3_lib::LittleEndian2Int(fread($this->getid3->fp, 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.fread($this->getid3->fp, 2);
     //					$ExtensionBlock = array();
     //					$ExtensionBlock['function_code']  = GetId3_lib::LittleEndian2Int(substr($ExtensionBlockData, 1, 1));
     //					$ExtensionBlock['byte_length']    = GetId3_lib::LittleEndian2Int(substr($ExtensionBlockData, 2, 1));
     //					$ExtensionBlock['data']           = fread($this->getid3->fp, $ExtensionBlock['byte_length']);
     //					$info['gif']['extension_blocks'][] = $ExtensionBlock;
     //					break;
     //
     //				case ';':
     //					$info['gif']['terminator_offset'] = ftell($this->getid3->fp) - 1;
     //					// GIF Terminator
     //					break;
     //
     //				default:
     //					break;
     //
     //
     //			}
     //		}
     return true;
 }
Exemple #21
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $LPACheader = fread($this->getid3->fp, 14);
     if (substr($LPACheader, 0, 4) != 'LPAC') {
         $info['error'][] = 'Expected "LPAC" at offset ' . $info['avdataoffset'] . ', found "' . $StreamMarker . '"';
         return false;
     }
     $info['avdataoffset'] += 14;
     $info['fileformat'] = 'lpac';
     $info['audio']['dataformat'] = 'lpac';
     $info['audio']['lossless'] = true;
     $info['audio']['bitrate_mode'] = 'vbr';
     $info['lpac']['file_version'] = Helper::BigEndian2Int(substr($LPACheader, 4, 1));
     $flags['audio_type'] = Helper::BigEndian2Int(substr($LPACheader, 5, 1));
     $info['lpac']['total_samples'] = Helper::BigEndian2Int(substr($LPACheader, 6, 4));
     $flags['parameters'] = Helper::BigEndian2Int(substr($LPACheader, 10, 4));
     $info['lpac']['flags']['is_wave'] = (bool) ($flags['audio_type'] & 0x40);
     $info['lpac']['flags']['stereo'] = (bool) ($flags['audio_type'] & 0x4);
     $info['lpac']['flags']['24_bit'] = (bool) ($flags['audio_type'] & 0x2);
     $info['lpac']['flags']['16_bit'] = (bool) ($flags['audio_type'] & 0x1);
     if ($info['lpac']['flags']['24_bit'] && $info['lpac']['flags']['16_bit']) {
         $info['warning'][] = '24-bit and 16-bit flags cannot both be set';
     }
     $info['lpac']['flags']['fast_compress'] = (bool) ($flags['parameters'] & 0x40000000);
     $info['lpac']['flags']['random_access'] = (bool) ($flags['parameters'] & 0x8000000);
     $info['lpac']['block_length'] = pow(2, ($flags['parameters'] & 0x7000000) >> 24) * 256;
     $info['lpac']['flags']['adaptive_prediction_order'] = (bool) ($flags['parameters'] & 0x800000);
     $info['lpac']['flags']['adaptive_quantization'] = (bool) ($flags['parameters'] & 0x400000);
     $info['lpac']['flags']['joint_stereo'] = (bool) ($flags['parameters'] & 0x40000);
     $info['lpac']['quantization'] = ($flags['parameters'] & 0x1f00) >> 8;
     $info['lpac']['max_prediction_order'] = $flags['parameters'] & 0x3f;
     if ($info['lpac']['flags']['fast_compress'] && $info['lpac']['max_prediction_order'] != 3) {
         $info['warning'][] = 'max_prediction_order expected to be "3" if fast_compress is true, actual value is "' . $info['lpac']['max_prediction_order'] . '"';
     }
     switch ($info['lpac']['file_version']) {
         case 6:
             if ($info['lpac']['flags']['adaptive_quantization']) {
                 $info['warning'][] = 'adaptive_quantization expected to be false in LPAC file stucture v6, actually true';
             }
             if ($info['lpac']['quantization'] != 20) {
                 $info['warning'][] = 'Quantization expected to be 20 in LPAC file stucture v6, actually ' . $info['lpac']['flags']['Q'];
             }
             break;
         default:
             //$info['warning'][] = 'This version of GetId3Core() ['.$this->getid3->version().'] only supports LPAC file format version 6, this file is version '.$info['lpac']['file_version'].' - please report to info@getid3.org';
             break;
     }
     $getid3_temp = new GetId3Core();
     $getid3_temp->openfile($this->getid3->filename);
     $getid3_temp->info = $info;
     $getid3_riff = new Riff($getid3_temp);
     $getid3_riff->analyze();
     $info['avdataoffset'] = $getid3_temp->info['avdataoffset'];
     $info['riff'] = $getid3_temp->info['riff'];
     $info['error'] = $getid3_temp->info['error'];
     $info['warning'] = $getid3_temp->info['warning'];
     $info['lpac']['comments']['comment'] = $getid3_temp->info['comments'];
     $info['audio']['sample_rate'] = $getid3_temp->info['audio']['sample_rate'];
     unset($getid3_temp, $getid3_riff);
     $info['audio']['channels'] = $info['lpac']['flags']['stereo'] ? 2 : 1;
     if ($info['lpac']['flags']['24_bit']) {
         $info['audio']['bits_per_sample'] = $info['riff']['audio'][0]['bits_per_sample'];
     } elseif ($info['lpac']['flags']['16_bit']) {
         $info['audio']['bits_per_sample'] = 16;
     } else {
         $info['audio']['bits_per_sample'] = 8;
     }
     if ($info['lpac']['flags']['fast_compress']) {
         // fast
         $info['audio']['encoder_options'] = '-1';
     } else {
         switch ($info['lpac']['max_prediction_order']) {
             case 20:
                 // simple
                 $info['audio']['encoder_options'] = '-2';
                 break;
             case 30:
                 // medium
                 $info['audio']['encoder_options'] = '-3';
                 break;
             case 40:
                 // high
                 $info['audio']['encoder_options'] = '-4';
                 break;
             case 60:
                 // extrahigh
                 $info['audio']['encoder_options'] = '-5';
                 break;
         }
     }
     $info['playtime_seconds'] = $info['lpac']['total_samples'] / $info['audio']['sample_rate'];
     $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
     return true;
 }
Exemple #22
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     if (!Helper::intValueSupported($info['filesize'])) {
         $info['warning'][] = 'Unable to check for ID3v1 because file is larger than ' . round(PHP_INT_MAX / 1073741824) . 'GB';
         return false;
     }
     fseek($this->getid3->fp, -256, SEEK_END);
     $preid3v1 = fread($this->getid3->fp, 128);
     $id3v1tag = fread($this->getid3->fp, 128);
     if (substr($id3v1tag, 0, 3) == 'TAG') {
         $info['avdataend'] = $info['filesize'] - 128;
         $ParsedID3v1['title'] = $this->cutfield(substr($id3v1tag, 3, 30));
         $ParsedID3v1['artist'] = $this->cutfield(substr($id3v1tag, 33, 30));
         $ParsedID3v1['album'] = $this->cutfield(substr($id3v1tag, 63, 30));
         $ParsedID3v1['year'] = $this->cutfield(substr($id3v1tag, 93, 4));
         $ParsedID3v1['comment'] = substr($id3v1tag, 97, 30);
         // can't remove nulls yet, track detection depends on them
         $ParsedID3v1['genreid'] = ord(substr($id3v1tag, 127, 1));
         // If second-last byte of comment field is null and last byte of comment field is non-null
         // then this is ID3v1.1 and the comment field is 28 bytes long and the 30th byte is the track number
         if ($id3v1tag[125] === "" && $id3v1tag[126] !== "") {
             $ParsedID3v1['track'] = ord(substr($ParsedID3v1['comment'], 29, 1));
             $ParsedID3v1['comment'] = substr($ParsedID3v1['comment'], 0, 28);
         }
         $ParsedID3v1['comment'] = $this->cutfield($ParsedID3v1['comment']);
         $ParsedID3v1['genre'] = $this->LookupGenreName($ParsedID3v1['genreid']);
         if (!empty($ParsedID3v1['genre'])) {
             unset($ParsedID3v1['genreid']);
         }
         if (isset($ParsedID3v1['genre']) && (empty($ParsedID3v1['genre']) || $ParsedID3v1['genre'] == 'Unknown')) {
             unset($ParsedID3v1['genre']);
         }
         foreach ($ParsedID3v1 as $key => $value) {
             $ParsedID3v1['comments'][$key][0] = $value;
         }
         // ID3v1 data is supposed to be padded with NULL characters, but some taggers pad with spaces
         $GoodFormatID3v1tag = $this->GenerateID3v1Tag($ParsedID3v1['title'], $ParsedID3v1['artist'], $ParsedID3v1['album'], $ParsedID3v1['year'], isset($ParsedID3v1['genre']) ? $this->LookupGenreID($ParsedID3v1['genre']) : false, $ParsedID3v1['comment'], !empty($ParsedID3v1['track']) ? $ParsedID3v1['track'] : '');
         $ParsedID3v1['padding_valid'] = true;
         if ($id3v1tag !== $GoodFormatID3v1tag) {
             $ParsedID3v1['padding_valid'] = false;
             $info['warning'][] = 'Some ID3v1 fields do not use NULL characters for padding';
         }
         $ParsedID3v1['tag_offset_end'] = $info['filesize'];
         $ParsedID3v1['tag_offset_start'] = $ParsedID3v1['tag_offset_end'] - 128;
         $info['id3v1'] = $ParsedID3v1;
     }
     if (substr($preid3v1, 0, 3) == 'TAG') {
         // The way iTunes handles tags is, well, brain-damaged.
         // It completely ignores v1 if ID3v2 is present.
         // This goes as far as adding a new v1 tag *even if there already is one*
         // A suspected double-ID3v1 tag has been detected, but it could be that
         // the "TAG" identifier is a legitimate part of an APE or Lyrics3 tag
         if (substr($preid3v1, 96, 8) == 'APETAGEX') {
             // an APE tag footer was found before the last ID3v1, assume false "TAG" synch
         } elseif (substr($preid3v1, 119, 6) == 'LYRICS') {
             // a Lyrics3 tag footer was found before the last ID3v1, assume false "TAG" synch
         } else {
             // APE and Lyrics3 footers not found - assume double ID3v1
             $info['warning'][] = 'Duplicate ID3v1 tag detected - this has been known to happen with iTunes';
             $info['avdataend'] -= 128;
         }
     }
     return true;
 }
Exemple #23
0
 /**
  * @return bool
  */
 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'];
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $MACheaderData = fread($this->getid3->fp, 74);
     $thisfile_monkeysaudio_raw['magic'] = substr($MACheaderData, 0, 4);
     $magic = 'MAC ';
     if ($thisfile_monkeysaudio_raw['magic'] != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($thisfile_monkeysaudio_raw['magic']) . '"';
         unset($info['fileformat']);
         return false;
     }
     $thisfile_monkeysaudio_raw['nVersion'] = Helper::LittleEndian2Int(substr($MACheaderData, 4, 2));
     // appears to be uint32 in 3.98+
     if ($thisfile_monkeysaudio_raw['nVersion'] < 3980) {
         $thisfile_monkeysaudio_raw['nCompressionLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, 6, 2));
         $thisfile_monkeysaudio_raw['nFormatFlags'] = Helper::LittleEndian2Int(substr($MACheaderData, 8, 2));
         $thisfile_monkeysaudio_raw['nChannels'] = Helper::LittleEndian2Int(substr($MACheaderData, 10, 2));
         $thisfile_monkeysaudio_raw['nSampleRate'] = Helper::LittleEndian2Int(substr($MACheaderData, 12, 4));
         $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, 16, 4));
         $thisfile_monkeysaudio_raw['nWAVTerminatingBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, 20, 4));
         $thisfile_monkeysaudio_raw['nTotalFrames'] = Helper::LittleEndian2Int(substr($MACheaderData, 24, 4));
         $thisfile_monkeysaudio_raw['nFinalFrameSamples'] = Helper::LittleEndian2Int(substr($MACheaderData, 28, 4));
         $thisfile_monkeysaudio_raw['nPeakLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, 32, 4));
         $thisfile_monkeysaudio_raw['nSeekElements'] = Helper::LittleEndian2Int(substr($MACheaderData, 38, 2));
         $offset = 8;
     } else {
         $offset = 8;
         // APE_DESCRIPTOR
         $thisfile_monkeysaudio_raw['nDescriptorBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nHeaderBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nSeekTableBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nHeaderDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nAPEFrameDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nAPEFrameDataBytesHigh'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nTerminatingDataBytes'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['cFileMD5'] = substr($MACheaderData, $offset, 16);
         $offset += 16;
         // APE_HEADER
         $thisfile_monkeysaudio_raw['nCompressionLevel'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nFormatFlags'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nBlocksPerFrame'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nFinalFrameBlocks'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nTotalFrames'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 4));
         $offset += 4;
         $thisfile_monkeysaudio_raw['nBitsPerSample'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nChannels'] = Helper::LittleEndian2Int(substr($MACheaderData, $offset, 2));
         $offset += 2;
         $thisfile_monkeysaudio_raw['nSampleRate'] = Helper::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;
 }
Exemple #24
0
 /**
  * @param  type $atom_data
  *
  * @return type
  *
  * @link http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#NCTG
  */
 public function QuicktimeParseNikonNCTG($atom_data)
 {
     // Nikon-specific QuickTime tags found in the NCDT atom of MOV videos from some Nikon cameras such as the Coolpix S8000 and D5100
     // Data is stored as records of:
     // * 4 bytes record type
     // * 2 bytes size of data field type:
     //     0x0001 = flag   (size field *= 1-byte)
     //     0x0002 = char   (size field *= 1-byte)
     //     0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
     //     0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
     //     0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
     //     0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
     //     0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
     // * 2 bytes data size field
     // * ? bytes data (string data may be null-padded; datestamp fields are in the format "2011:05:25 20:24:15")
     // all integers are stored BigEndian
     $NCTGtagName = array(0x1 => 'Make', 0x2 => 'Model', 0x3 => 'Software', 0x11 => 'CreateDate', 0x12 => 'DateTimeOriginal', 0x13 => 'FrameCount', 0x16 => 'FrameRate', 0x22 => 'FrameWidth', 0x23 => 'FrameHeight', 0x32 => 'AudioChannels', 0x33 => 'AudioBitsPerSample', 0x34 => 'AudioSampleRate', 0x2000001 => 'MakerNoteVersion', 0x2000005 => 'WhiteBalance', 0x200000b => 'WhiteBalanceFineTune', 0x200001e => 'ColorSpace', 0x2000023 => 'PictureControlData', 0x2000024 => 'WorldTime', 0x2000032 => 'UnknownInfo', 0x2000083 => 'LensType', 0x2000084 => 'Lens');
     $offset = 0;
     $datalength = strlen($atom_data);
     $parsed = array();
     while ($offset < $datalength) {
         //echo GetId3_lib::PrintHexBytes(substr($atom_data, $offset, 4)).'<br>';
         $record_type = Helper::BigEndian2Int(substr($atom_data, $offset, 4));
         $offset += 4;
         $data_size_type = Helper::BigEndian2Int(substr($atom_data, $offset, 2));
         $offset += 2;
         $data_size = Helper::BigEndian2Int(substr($atom_data, $offset, 2));
         $offset += 2;
         switch ($data_size_type) {
             case 0x1:
                 // 0x0001 = flag   (size field *= 1-byte)
                 $data = Helper::BigEndian2Int(substr($atom_data, $offset, $data_size * 1));
                 $offset += $data_size * 1;
                 break;
             case 0x2:
                 // 0x0002 = char   (size field *= 1-byte)
                 $data = substr($atom_data, $offset, $data_size * 1);
                 $offset += $data_size * 1;
                 $data = rtrim($data, "");
                 break;
             case 0x3:
                 // 0x0003 = DWORD+ (size field *= 2-byte), values are stored CDAB
                 $data = '';
                 for ($i = $data_size - 1; $i >= 0; --$i) {
                     $data .= substr($atom_data, $offset + $i * 2, 2);
                 }
                 $data = Helper::BigEndian2Int($data);
                 $offset += $data_size * 2;
                 break;
             case 0x4:
                 // 0x0004 = QWORD+ (size field *= 4-byte), values are stored EFGHABCD
                 $data = '';
                 for ($i = $data_size - 1; $i >= 0; --$i) {
                     $data .= substr($atom_data, $offset + $i * 4, 4);
                 }
                 $data = Helper::BigEndian2Int($data);
                 $offset += $data_size * 4;
                 break;
             case 0x5:
                 // 0x0005 = float  (size field *= 8-byte), values are stored aaaabbbb where value is aaaa/bbbb; possibly multiple sets of values appended together
                 $data = array();
                 for ($i = 0; $i < $data_size; ++$i) {
                     $numerator = Helper::BigEndian2Int(substr($atom_data, $offset + $i * 8 + 0, 4));
                     $denomninator = Helper::BigEndian2Int(substr($atom_data, $offset + $i * 8 + 4, 4));
                     if ($denomninator == 0) {
                         $data[$i] = false;
                     } else {
                         $data[$i] = (double) $numerator / $denomninator;
                     }
                 }
                 $offset += 8 * $data_size;
                 if (count($data) == 1) {
                     $data = $data[0];
                 }
                 break;
             case 0x7:
                 // 0x0007 = bytes  (size field *= 1-byte), values are stored as ??????
                 $data = substr($atom_data, $offset, $data_size * 1);
                 $offset += $data_size * 1;
                 break;
             case 0x8:
                 // 0x0008 = ?????  (size field *= 2-byte), values are stored as ??????
                 $data = substr($atom_data, $offset, $data_size * 2);
                 $offset += $data_size * 2;
                 break;
             default:
                 echo 'QuicktimeParseNikonNCTG()::unknown $data_size_type: ' . $data_size_type . '<br>';
                 break 2;
         }
         switch ($record_type) {
             case 0x11:
                 // CreateDate
             // CreateDate
             case 0x12:
                 // DateTimeOriginal
                 $data = strtotime($data);
                 break;
             case 0x200001e:
                 // ColorSpace
                 switch ($data) {
                     case 1:
                         $data = 'sRGB';
                         break;
                     case 2:
                         $data = 'Adobe RGB';
                         break;
                 }
                 break;
             case 0x2000023:
                 // PictureControlData
                 $PictureControlAdjust = array(0 => 'default', 1 => 'quick', 2 => 'full');
                 $FilterEffect = array(0x80 => 'off', 0x81 => 'yellow', 0x82 => 'orange', 0x83 => 'red', 0x84 => 'green', 0xff => 'n/a');
                 $ToningEffect = array(0x80 => 'b&w', 0x81 => 'sepia', 0x82 => 'cyanotype', 0x83 => 'red', 0x84 => 'yellow', 0x85 => 'green', 0x86 => 'blue-green', 0x87 => 'blue', 0x88 => 'purple-blue', 0x89 => 'red-purple', 0xff => 'n/a');
                 $data = array('PictureControlVersion' => substr($data, 0, 4), 'PictureControlName' => rtrim(substr($data, 4, 20), ""), 'PictureControlBase' => rtrim(substr($data, 24, 20), ""), 'PictureControlAdjust' => $PictureControlAdjust[ord(substr($data, 48, 1))], 'PictureControlQuickAdjust' => ord(substr($data, 49, 1)), 'Sharpness' => ord(substr($data, 50, 1)), 'Contrast' => ord(substr($data, 51, 1)), 'Brightness' => ord(substr($data, 52, 1)), 'Saturation' => ord(substr($data, 53, 1)), 'HueAdjustment' => ord(substr($data, 54, 1)), 'FilterEffect' => $FilterEffect[ord(substr($data, 55, 1))], 'ToningEffect' => $ToningEffect[ord(substr($data, 56, 1))], 'ToningSaturation' => ord(substr($data, 57, 1)));
                 break;
             case 0x2000024:
                 // WorldTime
                 // http://www.sno.phy.queensu.ca/~phil/exiftool/TagNames/Nikon.html#WorldTime
                 // timezone is stored as offset from GMT in minutes
                 $timezone = Helper::BigEndian2Int(substr($data, 0, 2));
                 if ($timezone & 0x8000) {
                     $timezone = 0 - (0x10000 - $timezone);
                 }
                 $timezone /= 60;
                 $dst = (bool) Helper::BigEndian2Int(substr($data, 2, 1));
                 switch (Helper::BigEndian2Int(substr($data, 3, 1))) {
                     case 2:
                         $datedisplayformat = 'D/M/Y';
                         break;
                     case 1:
                         $datedisplayformat = 'M/D/Y';
                         break;
                     case 0:
                     default:
                         $datedisplayformat = 'Y/M/D';
                         break;
                 }
                 $data = array('timezone' => floatval($timezone), 'dst' => $dst, 'display' => $datedisplayformat);
                 break;
             case 0x2000083:
                 // LensType
                 $data = array('mf' => (bool) ($data & 0x1), 'd' => (bool) ($data & 0x2), 'g' => (bool) ($data & 0x4), 'vr' => (bool) ($data & 0x8));
                 break;
         }
         $tag_name = isset($NCTGtagName[$record_type]) ? $NCTGtagName[$record_type] : '0x' . str_pad(dechex($record_type), 8, '0', STR_PAD_LEFT);
         $parsed[$tag_name] = $data;
     }
     return $parsed;
 }
Exemple #25
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     // based loosely on code from TTwinVQ by Jurgen Faul <jfaulØgmx*de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     $info['fileformat'] = 'vqf';
     $info['audio']['dataformat'] = 'vqf';
     $info['audio']['bitrate_mode'] = 'cbr';
     $info['audio']['lossless'] = false;
     // shortcut
     $info['vqf']['raw'] = array();
     $thisfile_vqf =& $info['vqf'];
     $thisfile_vqf_raw =& $thisfile_vqf['raw'];
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $VQFheaderData = fread($this->getid3->fp, 16);
     $offset = 0;
     $thisfile_vqf_raw['header_tag'] = substr($VQFheaderData, $offset, 4);
     $magic = 'TWIN';
     if ($thisfile_vqf_raw['header_tag'] != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($thisfile_vqf_raw['header_tag']) . '"';
         unset($info['vqf']);
         unset($info['fileformat']);
         return false;
     }
     $offset += 4;
     $thisfile_vqf_raw['version'] = substr($VQFheaderData, $offset, 8);
     $offset += 8;
     $thisfile_vqf_raw['size'] = Helper::BigEndian2Int(substr($VQFheaderData, $offset, 4));
     $offset += 4;
     while (ftell($this->getid3->fp) < $info['avdataend']) {
         $ChunkBaseOffset = ftell($this->getid3->fp);
         $chunkoffset = 0;
         $ChunkData = fread($this->getid3->fp, 8);
         $ChunkName = substr($ChunkData, $chunkoffset, 4);
         if ($ChunkName == 'DATA') {
             $info['avdataoffset'] = $ChunkBaseOffset;
             break;
         }
         $chunkoffset += 4;
         $ChunkSize = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
         $chunkoffset += 4;
         if ($ChunkSize > $info['avdataend'] - ftell($this->getid3->fp)) {
             $info['error'][] = 'Invalid chunk size (' . $ChunkSize . ') for chunk "' . $ChunkName . '" at offset ' . $ChunkBaseOffset;
             break;
         }
         if ($ChunkSize > 0) {
             $ChunkData .= fread($this->getid3->fp, $ChunkSize);
         }
         switch ($ChunkName) {
             case 'COMM':
                 // shortcut
                 $thisfile_vqf['COMM'] = array();
                 $thisfile_vqf_COMM =& $thisfile_vqf['COMM'];
                 $thisfile_vqf_COMM['channel_mode'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['bitrate'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['sample_rate'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $thisfile_vqf_COMM['security_level'] = Helper::BigEndian2Int(substr($ChunkData, $chunkoffset, 4));
                 $chunkoffset += 4;
                 $info['audio']['channels'] = $thisfile_vqf_COMM['channel_mode'] + 1;
                 $info['audio']['sample_rate'] = $this->VQFchannelFrequencyLookup($thisfile_vqf_COMM['sample_rate']);
                 $info['audio']['bitrate'] = $thisfile_vqf_COMM['bitrate'] * 1000;
                 $info['audio']['encoder_options'] = 'CBR' . ceil($info['audio']['bitrate'] / 1000);
                 if ($info['audio']['bitrate'] == 0) {
                     $info['error'][] = 'Corrupt VQF file: bitrate_audio == zero';
                     return false;
                 }
                 break;
             case 'NAME':
             case 'AUTH':
             case '(c) ':
             case 'FILE':
             case 'COMT':
             case 'ALBM':
                 $thisfile_vqf['comments'][$this->VQFcommentNiceNameLookup($ChunkName)][] = trim(substr($ChunkData, 8));
                 break;
             case 'DSIZ':
                 $thisfile_vqf['DSIZ'] = Helper::BigEndian2Int(substr($ChunkData, 8, 4));
                 break;
             default:
                 $info['warning'][] = 'Unhandled chunk type "' . $ChunkName . '" at offset ' . $ChunkBaseOffset;
                 break;
         }
     }
     $info['playtime_seconds'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['audio']['bitrate'];
     if (isset($thisfile_vqf['DSIZ']) && $thisfile_vqf['DSIZ'] != $info['avdataend'] - $info['avdataoffset'] - strlen('DATA')) {
         switch ($thisfile_vqf['DSIZ']) {
             case 0:
             case 1:
                 $info['warning'][] = 'Invalid DSIZ value "' . $thisfile_vqf['DSIZ'] . '". This is known to happen with VQF files encoded by Ahead Nero, and seems to be its way of saying this is TwinVQF v' . ($thisfile_vqf['DSIZ'] + 1) . '.0';
                 $info['audio']['encoder'] = 'Ahead Nero';
                 break;
             default:
                 $info['warning'][] = 'Probable corrupted file - should be ' . $thisfile_vqf['DSIZ'] . ' bytes, actually ' . ($info['avdataend'] - $info['avdataoffset'] - strlen('DATA'));
                 break;
         }
     }
     return true;
 }
Exemple #26
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     $OriginalAVdataOffset = $info['avdataoffset'];
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $VOCheader = fread($this->getid3->fp, 26);
     $magic = 'Creative Voice File';
     if (substr($VOCheader, 0, 19) != $magic) {
         $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::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'] = Helper::LittleEndian2Int(substr($VOCheader, 20, 2));
     $thisfile_voc['header']['minor_version'] = Helper::LittleEndian2Int(substr($VOCheader, 22, 1));
     $thisfile_voc['header']['major_version'] = Helper::LittleEndian2Int(substr($VOCheader, 23, 1));
     do {
         $BlockOffset = ftell($this->getid3->fp);
         $BlockData = fread($this->getid3->fp, 4);
         $BlockType = ord($BlockData[0]);
         $BlockSize = Helper::LittleEndian2Int(substr($BlockData, 1, 3));
         $ThisBlock = array();
         Helper::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 .= fread($this->getid3->fp, 2);
                 if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
                     $info['avdataoffset'] = ftell($this->getid3->fp);
                 }
                 fseek($this->getid3->fp, $BlockSize - 2, SEEK_CUR);
                 $ThisBlock['sample_rate_id'] = Helper::LittleEndian2Int(substr($BlockData, 4, 1));
                 $ThisBlock['compression_type'] = Helper::LittleEndian2Int(substr($BlockData, 5, 1));
                 $ThisBlock['compression_name'] = $this->VOCcompressionTypeLookup($ThisBlock['compression_type']);
                 if ($ThisBlock['compression_type'] <= 3) {
                     $thisfile_voc['compressed_bits_per_sample'] = Helper::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'] = Helper::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
                 fseek($this->getid3->fp, $BlockSize, SEEK_CUR);
                 break;
             case 8:
                 // Extended
                 $BlockData .= fread($this->getid3->fp, 4);
                 //00-01  Time Constant:
                 //   Mono: 65536 - (256000000 / sample_rate)
                 // Stereo: 65536 - (256000000 / (sample_rate * 2))
                 $ThisBlock['time_constant'] = Helper::LittleEndian2Int(substr($BlockData, 4, 2));
                 $ThisBlock['pack_method'] = Helper::LittleEndian2Int(substr($BlockData, 6, 1));
                 $ThisBlock['stereo'] = (bool) Helper::LittleEndian2Int(substr($BlockData, 7, 1));
                 $thisfile_audio['channels'] = $ThisBlock['stereo'] ? 2 : 1;
                 $thisfile_audio['sample_rate'] = Helper::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 .= fread($this->getid3->fp, 12);
                 if ($info['avdataoffset'] <= $OriginalAVdataOffset) {
                     $info['avdataoffset'] = ftell($this->getid3->fp);
                 }
                 fseek($this->getid3->fp, $BlockSize - 12, SEEK_CUR);
                 $ThisBlock['sample_rate'] = Helper::LittleEndian2Int(substr($BlockData, 4, 4));
                 $ThisBlock['bits_per_sample'] = Helper::LittleEndian2Int(substr($BlockData, 8, 1));
                 $ThisBlock['channels'] = Helper::LittleEndian2Int(substr($BlockData, 9, 1));
                 $ThisBlock['wFormat'] = Helper::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;
                 fseek($this->getid3->fp, $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
     fseek($this->getid3->fp, -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;
 }
Exemple #27
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     $info['fileformat'] = 'swf';
     $info['video']['dataformat'] = 'swf';
     // http://www.openswf.org/spec/SWFfileformat.html
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $SWFfileData = fread($this->getid3->fp, $info['avdataend'] - $info['avdataoffset']);
     // 8 + 2 + 2 + max(9) bytes NOT including Frame_Size RECT data
     $info['swf']['header']['signature'] = substr($SWFfileData, 0, 3);
     switch ($info['swf']['header']['signature']) {
         case 'FWS':
             $info['swf']['header']['compressed'] = false;
             break;
         case 'CWS':
             $info['swf']['header']['compressed'] = true;
             break;
         default:
             $info['error'][] = 'Expecting "FWS" or "CWS" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($info['swf']['header']['signature']) . '"';
             unset($info['swf']);
             unset($info['fileformat']);
             return false;
             break;
     }
     $info['swf']['header']['version'] = Helper::LittleEndian2Int(substr($SWFfileData, 3, 1));
     $info['swf']['header']['length'] = Helper::LittleEndian2Int(substr($SWFfileData, 4, 4));
     if ($info['swf']['header']['compressed']) {
         $SWFHead = substr($SWFfileData, 0, 8);
         $SWFfileData = substr($SWFfileData, 8);
         if ($decompressed = @gzuncompress($SWFfileData)) {
             $SWFfileData = $SWFHead . $decompressed;
         } else {
             $info['error'][] = 'Error decompressing compressed SWF data (' . strlen($SWFfileData) . ' bytes compressed, should be ' . ($info['swf']['header']['length'] - 8) . ' bytes uncompressed)';
             return false;
         }
     }
     $FrameSizeBitsPerValue = (ord(substr($SWFfileData, 8, 1)) & 0xf8) >> 3;
     $FrameSizeDataLength = ceil((5 + 4 * $FrameSizeBitsPerValue) / 8);
     $FrameSizeDataString = str_pad(decbin(ord(substr($SWFfileData, 8, 1)) & 0x7), 3, '0', STR_PAD_LEFT);
     for ($i = 1; $i < $FrameSizeDataLength; ++$i) {
         $FrameSizeDataString .= str_pad(decbin(ord(substr($SWFfileData, 8 + $i, 1))), 8, '0', STR_PAD_LEFT);
     }
     list($X1, $X2, $Y1, $Y2) = explode("\n", wordwrap($FrameSizeDataString, $FrameSizeBitsPerValue, "\n", 1));
     $info['swf']['header']['frame_width'] = Helper::Bin2Dec($X2);
     $info['swf']['header']['frame_height'] = Helper::Bin2Dec($Y2);
     // http://www-lehre.informatik.uni-osnabrueck.de/~fbstark/diplom/docs/swf/Flash_Uncovered.htm
     // Next in the header is the frame rate, which is kind of weird.
     // It is supposed to be stored as a 16bit integer, but the first byte
     // (or last depending on how you look at it) is completely ignored.
     // Example: 0x000C  ->  0x0C  ->  12     So the frame rate is 12 fps.
     // Byte at (8 + $FrameSizeDataLength) is always zero and ignored
     $info['swf']['header']['frame_rate'] = Helper::LittleEndian2Int(substr($SWFfileData, 9 + $FrameSizeDataLength, 1));
     $info['swf']['header']['frame_count'] = Helper::LittleEndian2Int(substr($SWFfileData, 10 + $FrameSizeDataLength, 2));
     $info['video']['frame_rate'] = $info['swf']['header']['frame_rate'];
     $info['video']['resolution_x'] = intval(round($info['swf']['header']['frame_width'] / 20));
     $info['video']['resolution_y'] = intval(round($info['swf']['header']['frame_height'] / 20));
     $info['video']['pixel_aspect_ratio'] = (double) 1;
     if ($info['swf']['header']['frame_count'] > 0 && $info['swf']['header']['frame_rate'] > 0) {
         $info['playtime_seconds'] = $info['swf']['header']['frame_count'] / $info['swf']['header']['frame_rate'];
     }
     //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
     // SWF tags
     $CurrentOffset = 12 + $FrameSizeDataLength;
     $SWFdataLength = strlen($SWFfileData);
     while ($CurrentOffset < $SWFdataLength) {
         //echo __LINE__.'='.number_format(microtime(true) - $start_time, 3).'<br>';
         $TagIDTagLength = Helper::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 2));
         $TagID = ($TagIDTagLength & 0xfffc) >> 6;
         $TagLength = $TagIDTagLength & 0x3f;
         $CurrentOffset += 2;
         if ($TagLength == 0x3f) {
             $TagLength = Helper::LittleEndian2Int(substr($SWFfileData, $CurrentOffset, 4));
             $CurrentOffset += 4;
         }
         unset($TagData);
         $TagData['offset'] = $CurrentOffset;
         $TagData['size'] = $TagLength;
         $TagData['id'] = $TagID;
         $TagData['data'] = substr($SWFfileData, $CurrentOffset, $TagLength);
         switch ($TagID) {
             case 0:
                 // end of movie
                 break 2;
             case 9:
                 // Set background color
                 //$info['swf']['tags'][] = $TagData;
                 $info['swf']['bgcolor'] = strtoupper(str_pad(dechex(Helper::BigEndian2Int($TagData['data'])), 6, '0', STR_PAD_LEFT));
                 break;
             default:
                 if ($this->ReturnAllTagData) {
                     $info['swf']['tags'][] = $TagData;
                 }
                 break;
         }
         $CurrentOffset += $TagLength;
     }
     return true;
 }
Exemple #28
0
 /**
  * @return bool
  */
 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;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     $offset = 0;
     $BMPheader = fread($this->getid3->fp, 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 "' . Helper::PrintHexBytes($magic) . '" at offset ' . $info['avdataoffset'] . ', found "' . Helper::PrintHexBytes($thisfile_bmp_header_raw['identifier']) . '"';
         unset($info['fileformat']);
         unset($info['bmp']);
         return false;
     }
     $thisfile_bmp_header_raw['filesize'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     $thisfile_bmp_header_raw['reserved1'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
     $offset += 2;
     $thisfile_bmp_header_raw['reserved2'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
     $offset += 2;
     $thisfile_bmp_header_raw['data_offset'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     $thisfile_bmp_header_raw['header_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
     $offset += 4;
     // check if the hardcoded-to-1 "planes" is at offset 22 or 26
     $planes22 = Helper::LittleEndian2Int(substr($BMPheader, 22, 2));
     $planes26 = Helper::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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['height'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['planes'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['bits_per_pixel'] = Helper::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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['bmp_data_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_h'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_v'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['colors_used'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['colors_important'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['resolution_units'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['reserved1'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['recording'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['rendering'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
             $offset += 2;
             $thisfile_bmp_header_raw['size1'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['size2'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['color_encoding'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['identifier'] = Helper::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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['height'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['planes'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['bits_per_pixel'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 2));
         $offset += 2;
         $thisfile_bmp_header_raw['compression'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['bmp_data_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['resolution_h'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['resolution_v'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4), true);
         $offset += 4;
         $thisfile_bmp_header_raw['colors_used'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
         $offset += 4;
         $thisfile_bmp_header_raw['colors_important'] = Helper::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 .= fread($this->getid3->fp, 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['green_mask'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['blue_mask'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['alpha_mask'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['cs_type'] = Helper::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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_green'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['gamma_blue'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header['ciexyz_red'] = Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_red']));
             $thisfile_bmp_header['ciexyz_green'] = Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_green']));
             $thisfile_bmp_header['ciexyz_blue'] = Helper::FixedPoint2_30(strrev($thisfile_bmp_header_raw['ciexyz_blue']));
         }
         if ($thisfile_bmp['type_version'] >= 5) {
             $BMPheader .= fread($this->getid3->fp, 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'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['profile_data_offset'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['profile_data_size'] = Helper::LittleEndian2Int(substr($BMPheader, $offset, 4));
             $offset += 4;
             $thisfile_bmp_header_raw['reserved3'] = Helper::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 = fread($this->getid3->fp, 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 = Helper::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 $green = Helper::LittleEndian2Int(substr($BMPpalette, $paletteoffset++, 1));
                 $red = Helper::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) {
         fseek($this->getid3->fp, $thisfile_bmp_header_raw['data_offset'], SEEK_SET);
         $RowByteLength = ceil($thisfile_bmp_header_raw['width'] * ($thisfile_bmp_header_raw['bits_per_pixel'] / 8) / 4) * 4;
         // round up to nearest DWORD boundry
         $BMPpixelData = fread($this->getid3->fp, $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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             $secondbyte = Helper::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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $rowincrement = Helper::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 = Helper::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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                             $secondbyte = Helper::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 = Helper::LittleEndian2Int(substr($BMPpixelData, $pixeldataoffset++, 1));
                                         $rowincrement = Helper::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 = Helper::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 = Helper::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;
 }
Exemple #29
0
 /**
  * @return bool
  */
 public function analyze()
 {
     $info =& $this->getid3->info;
     fseek($this->getid3->fp, $info['avdataoffset'], SEEK_SET);
     while (true) {
         $wavpackheader = fread($this->getid3->fp, 32);
         if (ftell($this->getid3->fp) >= $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 = ftell($this->getid3->fp) - 32;
         $blockheader_magic = substr($wavpackheader, 0, 4);
         $blockheader_size = Helper::LittleEndian2Int(substr($wavpackheader, 4, 4));
         $magic = 'wvpk';
         if ($blockheader_magic != $magic) {
             $info['error'][] = 'Expecting "' . Helper::PrintHexBytes($magic) . '" at offset ' . $blockheader_offset . ', found "' . Helper::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'] = Helper::LittleEndian2Int(substr($wavpackheader, 12, 4));
             $info['wavpack']['blockheader']['block_index'] = Helper::LittleEndian2Int(substr($wavpackheader, 16, 4));
             $info['wavpack']['blockheader']['block_samples'] = Helper::LittleEndian2Int(substr($wavpackheader, 20, 4));
             $info['wavpack']['blockheader']['flags_raw'] = Helper::LittleEndian2Int(substr($wavpackheader, 24, 4));
             $info['wavpack']['blockheader']['crc'] = Helper::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) && ftell($this->getid3->fp) < $blockheader_offset + $blockheader_size + 8) {
             $metablock = array('offset' => ftell($this->getid3->fp));
             $metablockheader = fread($this->getid3->fp, 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 .= fread($this->getid3->fp, 2);
             }
             $metablock['size'] = Helper::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'] = fread($this->getid3->fp, $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
                         fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
                         break;
                     default:
                         $info['warning'][] = 'Unexpected metablock type "0x' . str_pad(dechex($metablock['function_id']), 2, '0', STR_PAD_LEFT) . '" at offset ' . $metablock['offset'];
                         fseek($this->getid3->fp, $metablock['offset'] + ($metablock['large_block'] ? 4 : 2) + $metablock['size'], SEEK_SET);
                         break;
                 }
                 switch ($metablock['function_id']) {
                     case 0x21:
                         // ID_RIFF_HEADER
                         $original_wav_filesize = Helper::LittleEndian2Int(substr($metablock['data'], 4, 4));
                         $getid3_temp = new GetId3Core();
                         $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 GetId3Core();
                         $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'])) {
                             $getid3_riff->RIFFcommentsParse($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 GetId3Core() in metablock at offset ' . $metablock['offset'];
                         break;
                     case 0x24:
                         // ID_CUESHEET
                         $info['warning'][] = 'WavPack "Cuesheet" contents not yet handled by GetId3Core() in metablock at offset ' . $metablock['offset'];
                         break;
                     case 0x25:
                         // ID_CONFIG_BLOCK
                         $metablock['flags_raw'] = Helper::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(Helper::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;
 }
Exemple #30
0
 /**
  * @param  type $byteword
  * @param  type $signed
  *
  * @return type
  */
 public function EitherEndian2Int($byteword, $signed = false)
 {
     if ($this->getid3->info['fileformat'] == 'riff') {
         return Helper::LittleEndian2Int($byteword, $signed);
     }
     return Helper::BigEndian2Int($byteword, false, $signed);
 }