Exemple #1
0
 /**
  * @staticvar type $MPEGaudioVersionLookup
  * @staticvar type $MPEGaudioLayerLookup
  * @staticvar type $MPEGaudioBitrateLookup
  *
  * @param  type    $avdataoffset
  * @param  bool $BitrateHistogram
  *
  * @return bool
  */
 public function getOnlyMPEGaudioInfo($avdataoffset, $BitrateHistogram = false)
 {
     // looks for synch, decodes MPEG audio header
     $info =& $this->getGetId3()->info;
     static $MPEGaudioVersionLookup;
     static $MPEGaudioLayerLookup;
     static $MPEGaudioBitrateLookup;
     if (empty($MPEGaudioVersionLookup)) {
         $MPEGaudioVersionLookup = self::MPEGaudioVersionArray();
         $MPEGaudioLayerLookup = self::MPEGaudioLayerArray();
         $MPEGaudioBitrateLookup = self::MPEGaudioBitrateArray();
     }
     fseek($this->getGetId3()->fp, $avdataoffset, SEEK_SET);
     $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset);
     if ($sync_seek_buffer_size <= 0) {
         $info['error'][] = 'Invalid $sync_seek_buffer_size at offset ' . $avdataoffset;
         return false;
     }
     $header = fread($this->getGetId3()->fp, $sync_seek_buffer_size);
     $sync_seek_buffer_size = strlen($header);
     $SynchSeekOffset = 0;
     while ($SynchSeekOffset < $sync_seek_buffer_size) {
         if ($avdataoffset + $SynchSeekOffset < $info['avdataend'] && !feof($this->getGetId3()->fp)) {
             if ($SynchSeekOffset > $sync_seek_buffer_size) {
                 // if a synch's not found within the first 128k bytes, then give up
                 $info['error'][] = 'Could not find valid MPEG audio synch within the first ' . round($sync_seek_buffer_size / 1024) . 'kB';
                 if (isset($info['audio']['bitrate'])) {
                     unset($info['audio']['bitrate']);
                 }
                 if (isset($info['mpeg']['audio'])) {
                     unset($info['mpeg']['audio']);
                 }
                 if (empty($info['mpeg'])) {
                     unset($info['mpeg']);
                 }
                 return false;
             } elseif (feof($this->getGetId3()->fp)) {
                 $info['error'][] = 'Could not find valid MPEG audio synch before end of file';
                 if (isset($info['audio']['bitrate'])) {
                     unset($info['audio']['bitrate']);
                 }
                 if (isset($info['mpeg']['audio'])) {
                     unset($info['mpeg']['audio']);
                 }
                 if (isset($info['mpeg']) && (!is_array($info['mpeg']) || count($info['mpeg']) == 0)) {
                     unset($info['mpeg']);
                 }
                 return false;
             }
         }
         if ($SynchSeekOffset + 1 >= strlen($header)) {
             $info['error'][] = 'Could not find valid MPEG synch before end of file';
             return false;
         }
         if ($header[$SynchSeekOffset] == "ÿ" && $header[$SynchSeekOffset + 1] > "à") {
             // synch detected
             if (!isset($FirstFrameThisfileInfo) && !isset($info['mpeg']['audio'])) {
                 $FirstFrameThisfileInfo = $info;
                 $FirstFrameAVDataOffset = $avdataoffset + $SynchSeekOffset;
                 if (!$this->decodeMPEGaudioHeader($FirstFrameAVDataOffset, $FirstFrameThisfileInfo, false)) {
                     // if this is the first valid MPEG-audio frame, save it in case it's a VBR header frame and there's
                     // garbage between this frame and a valid sequence of MPEG-audio frames, to be restored below
                     unset($FirstFrameThisfileInfo);
                 }
             }
             $dummy = $info;
             // only overwrite real data if valid header found
             if ($this->decodeMPEGaudioHeader($avdataoffset + $SynchSeekOffset, $dummy, true)) {
                 $info = $dummy;
                 $info['avdataoffset'] = $avdataoffset + $SynchSeekOffset;
                 switch (isset($info['fileformat']) ? $info['fileformat'] : '') {
                     case '':
                     case 'id3':
                     case 'ape':
                     case 'mp3':
                         $info['fileformat'] = 'mp3';
                         $info['audio']['dataformat'] = 'mp3';
                         break;
                 }
                 if (isset($FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode']) && $FirstFrameThisfileInfo['mpeg']['audio']['bitrate_mode'] == 'vbr') {
                     if (!(abs($info['audio']['bitrate'] - $FirstFrameThisfileInfo['audio']['bitrate']) <= 1)) {
                         // If there is garbage data between a valid VBR header frame and a sequence
                         // of valid MPEG-audio frames the VBR data is no longer discarded.
                         $info = $FirstFrameThisfileInfo;
                         $info['avdataoffset'] = $FirstFrameAVDataOffset;
                         $info['fileformat'] = 'mp3';
                         $info['audio']['dataformat'] = 'mp3';
                         $dummy = $info;
                         unset($dummy['mpeg']['audio']);
                         $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength'];
                         $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset;
                         if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) {
                             $info = $dummy;
                             $info['avdataoffset'] = $GarbageOffsetEnd;
                             $info['warning'][] = 'apparently-valid VBR header not used because could not find ' . self::GETID3_MP3_VALID_CHECK_FRAMES . ' consecutive MPEG-audio frames immediately after VBR header (garbage data for ' . ($GarbageOffsetEnd - $GarbageOffsetStart) . ' bytes between ' . $GarbageOffsetStart . ' and ' . $GarbageOffsetEnd . '), but did find valid CBR stream starting at ' . $GarbageOffsetEnd;
                         } else {
                             $info['warning'][] = 'using data from VBR header even though could not find ' . self::GETID3_MP3_VALID_CHECK_FRAMES . ' consecutive MPEG-audio frames immediately after VBR header (garbage data for ' . ($GarbageOffsetEnd - $GarbageOffsetStart) . ' bytes between ' . $GarbageOffsetStart . ' and ' . $GarbageOffsetEnd . ')';
                         }
                     }
                 }
                 if (isset($info['mpeg']['audio']['bitrate_mode']) && $info['mpeg']['audio']['bitrate_mode'] == 'vbr' && !isset($info['mpeg']['audio']['VBR_method'])) {
                     // VBR file with no VBR header
                     $BitrateHistogram = true;
                 }
                 if ($BitrateHistogram) {
                     $info['mpeg']['audio']['stereo_distribution'] = array('stereo' => 0, 'joint stereo' => 0, 'dual channel' => 0, 'mono' => 0);
                     $info['mpeg']['audio']['version_distribution'] = array('1' => 0, '2' => 0, '2.5' => 0);
                     if ($info['mpeg']['audio']['version'] == '1') {
                         if ($info['mpeg']['audio']['layer'] == 3) {
                             $info['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0, 40000 => 0, 48000 => 0, 56000 => 0, 64000 => 0, 80000 => 0, 96000 => 0, 112000 => 0, 128000 => 0, 160000 => 0, 192000 => 0, 224000 => 0, 256000 => 0, 320000 => 0);
                         } elseif ($info['mpeg']['audio']['layer'] == 2) {
                             $info['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0, 48000 => 0, 56000 => 0, 64000 => 0, 80000 => 0, 96000 => 0, 112000 => 0, 128000 => 0, 160000 => 0, 192000 => 0, 224000 => 0, 256000 => 0, 320000 => 0, 384000 => 0);
                         } elseif ($info['mpeg']['audio']['layer'] == 1) {
                             $info['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0, 64000 => 0, 96000 => 0, 128000 => 0, 160000 => 0, 192000 => 0, 224000 => 0, 256000 => 0, 288000 => 0, 320000 => 0, 352000 => 0, 384000 => 0, 416000 => 0, 448000 => 0);
                         }
                     } elseif ($info['mpeg']['audio']['layer'] == 1) {
                         $info['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 32000 => 0, 48000 => 0, 56000 => 0, 64000 => 0, 80000 => 0, 96000 => 0, 112000 => 0, 128000 => 0, 144000 => 0, 160000 => 0, 176000 => 0, 192000 => 0, 224000 => 0, 256000 => 0);
                     } else {
                         $info['mpeg']['audio']['bitrate_distribution'] = array('free' => 0, 8000 => 0, 16000 => 0, 24000 => 0, 32000 => 0, 40000 => 0, 48000 => 0, 56000 => 0, 64000 => 0, 80000 => 0, 96000 => 0, 112000 => 0, 128000 => 0, 144000 => 0, 160000 => 0);
                     }
                     $dummy = array('error' => $info['error'], 'warning' => $info['warning'], 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']);
                     $synchstartoffset = $info['avdataoffset'];
                     fseek($this->getGetId3()->fp, $info['avdataoffset'], SEEK_SET);
                     // you can play with these numbers:
                     $max_frames_scan = 50000;
                     $max_scan_segments = 10;
                     // don't play with these numbers:
                     $FastMode = false;
                     $SynchErrorsFound = 0;
                     $frames_scanned = 0;
                     $this_scan_segment = 0;
                     $frames_scan_per_segment = ceil($max_frames_scan / $max_scan_segments);
                     $pct_data_scanned = 0;
                     for ($current_segment = 0; $current_segment < $max_scan_segments; ++$current_segment) {
                         $frames_scanned_this_segment = 0;
                         if (ftell($this->getGetId3()->fp) >= $info['avdataend']) {
                             break;
                         }
                         $scan_start_offset[$current_segment] = max(ftell($this->getGetId3()->fp), $info['avdataoffset'] + round($current_segment * (($info['avdataend'] - $info['avdataoffset']) / $max_scan_segments)));
                         if ($current_segment > 0) {
                             fseek($this->getGetId3()->fp, $scan_start_offset[$current_segment], SEEK_SET);
                             $buffer_4k = fread($this->getGetId3()->fp, 4096);
                             for ($j = 0; $j < strlen($buffer_4k) - 4; ++$j) {
                                 if ($buffer_4k[$j] == "ÿ" && $buffer_4k[$j + 1] > "à") {
                                     // synch detected
                                     if ($this->decodeMPEGaudioHeader($scan_start_offset[$current_segment] + $j, $dummy, false, false, $FastMode)) {
                                         $calculated_next_offset = $scan_start_offset[$current_segment] + $j + $dummy['mpeg']['audio']['framelength'];
                                         if ($this->decodeMPEGaudioHeader($calculated_next_offset, $dummy, false, false, $FastMode)) {
                                             $scan_start_offset[$current_segment] += $j;
                                             break;
                                         }
                                     }
                                 }
                             }
                         }
                         $synchstartoffset = $scan_start_offset[$current_segment];
                         while ($this->decodeMPEGaudioHeader($synchstartoffset, $dummy, false, false, $FastMode)) {
                             $FastMode = true;
                             $thisframebitrate = $MPEGaudioBitrateLookup[$MPEGaudioVersionLookup[$dummy['mpeg']['audio']['raw']['version']]][$MPEGaudioLayerLookup[$dummy['mpeg']['audio']['raw']['layer']]][$dummy['mpeg']['audio']['raw']['bitrate']];
                             if (empty($dummy['mpeg']['audio']['framelength'])) {
                                 ++$SynchErrorsFound;
                                 ++$synchstartoffset;
                             } else {
                                 Helper::safe_inc($info['mpeg']['audio']['bitrate_distribution'][$thisframebitrate]);
                                 Helper::safe_inc($info['mpeg']['audio']['stereo_distribution'][$dummy['mpeg']['audio']['channelmode']]);
                                 Helper::safe_inc($info['mpeg']['audio']['version_distribution'][$dummy['mpeg']['audio']['version']]);
                                 $synchstartoffset += $dummy['mpeg']['audio']['framelength'];
                             }
                             ++$frames_scanned;
                             if ($frames_scan_per_segment && ++$frames_scanned_this_segment >= $frames_scan_per_segment) {
                                 $this_pct_scanned = (ftell($this->getGetId3()->fp) - $scan_start_offset[$current_segment]) / ($info['avdataend'] - $info['avdataoffset']);
                                 if ($current_segment == 0 && $this_pct_scanned * $max_scan_segments >= 1) {
                                     // file likely contains < $max_frames_scan, just scan as one segment
                                     $max_scan_segments = 1;
                                     $frames_scan_per_segment = $max_frames_scan;
                                 } else {
                                     $pct_data_scanned += $this_pct_scanned;
                                     break;
                                 }
                             }
                         }
                     }
                     if ($pct_data_scanned > 0) {
                         $info['warning'][] = 'too many MPEG audio frames to scan, only scanned ' . $frames_scanned . ' frames in ' . $max_scan_segments . ' segments (' . number_format($pct_data_scanned * 100, 1) . '% of file) and extrapolated distribution, playtime and bitrate may be incorrect.';
                         foreach ($info['mpeg']['audio'] as $key1 => $value1) {
                             if (!preg_match('#_distribution$#i', $key1)) {
                                 continue;
                             }
                             foreach ($value1 as $key2 => $value2) {
                                 $info['mpeg']['audio'][$key1][$key2] = round($value2 / $pct_data_scanned);
                             }
                         }
                     }
                     if ($SynchErrorsFound > 0) {
                         $info['warning'][] = 'Found ' . $SynchErrorsFound . ' synch errors in histogram analysis';
                         //return false;
                     }
                     $bittotal = 0;
                     $framecounter = 0;
                     foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitratevalue => $bitratecount) {
                         $framecounter += $bitratecount;
                         if ($bitratevalue != 'free') {
                             $bittotal += $bitratevalue * $bitratecount;
                         }
                     }
                     if ($framecounter == 0) {
                         $info['error'][] = 'Corrupt MP3 file: framecounter == zero';
                         return false;
                     }
                     $info['mpeg']['audio']['frame_count'] = Helper::CastAsInt($framecounter);
                     $info['mpeg']['audio']['bitrate'] = $bittotal / $framecounter;
                     $info['audio']['bitrate'] = $info['mpeg']['audio']['bitrate'];
                     // Definitively set VBR vs CBR, even if the Xing/LAME/VBRI header says differently
                     $distinct_bitrates = 0;
                     foreach ($info['mpeg']['audio']['bitrate_distribution'] as $bitrate_value => $bitrate_count) {
                         if ($bitrate_count > 0) {
                             ++$distinct_bitrates;
                         }
                     }
                     if ($distinct_bitrates > 1) {
                         $info['mpeg']['audio']['bitrate_mode'] = 'vbr';
                     } else {
                         $info['mpeg']['audio']['bitrate_mode'] = 'cbr';
                     }
                     $info['audio']['bitrate_mode'] = $info['mpeg']['audio']['bitrate_mode'];
                 }
                 break;
                 // exit while()
             }
         }
         ++$SynchSeekOffset;
         if ($avdataoffset + $SynchSeekOffset >= $info['avdataend']) {
             // end of file/data
             if (empty($info['mpeg']['audio'])) {
                 $info['error'][] = 'could not find valid MPEG synch before end of file';
                 if (isset($info['audio']['bitrate'])) {
                     unset($info['audio']['bitrate']);
                 }
                 if (isset($info['mpeg']['audio'])) {
                     unset($info['mpeg']['audio']);
                 }
                 if (isset($info['mpeg']) && (!is_array($info['mpeg']) || empty($info['mpeg']))) {
                     unset($info['mpeg']);
                 }
                 return false;
             }
             break;
         }
     }
     $info['audio']['channels'] = $info['mpeg']['audio']['channels'];
     $info['audio']['channelmode'] = $info['mpeg']['audio']['channelmode'];
     $info['audio']['sample_rate'] = $info['mpeg']['audio']['sample_rate'];
     return true;
 }
Exemple #2
0
 /**
  * @param  type    $BlockData
  *
  * @return bool
  */
 private function parseSEEKTABLE($BlockData)
 {
     $info =& $this->getid3->info;
     $offset = 0;
     $BlockLength = strlen($BlockData);
     $placeholderpattern = str_repeat("ÿ", 8);
     while ($offset < $BlockLength) {
         $SampleNumberString = substr($BlockData, $offset, 8);
         $offset += 8;
         if ($SampleNumberString == $placeholderpattern) {
             // placeholder point
             Helper::safe_inc($info['flac']['SEEKTABLE']['placeholders'], 1);
             $offset += 10;
         } else {
             $SampleNumber = Helper::BigEndian2Int($SampleNumberString);
             $info['flac']['SEEKTABLE'][$SampleNumber]['offset'] = Helper::BigEndian2Int(substr($BlockData, $offset, 8));
             $offset += 8;
             $info['flac']['SEEKTABLE'][$SampleNumber]['samples'] = Helper::BigEndian2Int(substr($BlockData, $offset, 2));
             $offset += 2;
         }
     }
     return true;
 }
Exemple #3
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 #4
0
 /**
  * @staticvar array $decbin
  *
  * @param  type    $MaxFramesToScan
  * @param  type    $ReturnExtendedInfo
  *
  * @return bool
  */
 public function getAACADTSheaderFilepointer($MaxFramesToScan = 1000000, $ReturnExtendedInfo = false)
 {
     $info =& $this->getid3->info;
     // based loosely on code from AACfile by Jurgen Faul  <jfaulØgmx.de>
     // http://jfaul.de/atl  or  http://j-faul.virtualave.net/atl/atl.html
     // http://faac.sourceforge.net/wiki/index.php?page=ADTS // dead link
     // http://wiki.multimedia.cx/index.php?title=ADTS
     // * ADTS Fixed Header: these don't change from frame to frame
     // syncword                                       12    always: '111111111111'
     // ID                                              1    0: MPEG-4, 1: MPEG-2
     // MPEG layer                                      2    If you send AAC in MPEG-TS, set to 0
     // protection_absent                               1    0: CRC present; 1: no CRC
     // profile                                         2    0: AAC Main; 1: AAC LC (Low Complexity); 2: AAC SSR (Scalable Sample Rate); 3: AAC LTP (Long Term Prediction)
     // sampling_frequency_index                        4    15 not allowed
     // private_bit                                     1    usually 0
     // channel_configuration                           3
     // original/copy                                   1    0: original; 1: copy
     // home                                            1    usually 0
     // emphasis                                        2    only if ID == 0 (ie MPEG-4)  // not present in some documentation?
     // * ADTS Variable Header: these can change from frame to frame
     // copyright_identification_bit                    1
     // copyright_identification_start                  1
     // aac_frame_length                               13    length of the frame including header (in bytes)
     // adts_buffer_fullness                           11    0x7FF indicates VBR
     // no_raw_data_blocks_in_frame                     2
     // * ADTS Error check
     // crc_check                                      16    only if protection_absent == 0
     $byteoffset = $info['avdataoffset'];
     $framenumber = 0;
     // Init bit pattern array
     static $decbin = array();
     // Populate $bindec
     for ($i = 0; $i < 256; ++$i) {
         $decbin[chr($i)] = str_pad(decbin($i), 8, '0', STR_PAD_LEFT);
     }
     // used to calculate bitrate below
     $BitrateCache = array();
     while (true) {
         // breaks out when end-of-file encountered, or invalid data found,
         // or MaxFramesToScan frames have been scanned
         if (!Helper::intValueSupported($byteoffset)) {
             $info['warning'][] = 'Unable to parse AAC file beyond ' . ftell($this->getid3->fp) . ' (PHP does not support file operations beyond ' . round(PHP_INT_MAX / 1073741824) . 'GB)';
             return false;
         }
         fseek($this->getid3->fp, $byteoffset, SEEK_SET);
         // First get substring
         $substring = fread($this->getid3->fp, 9);
         // header is 7 bytes (or 9 if CRC is present)
         $substringlength = strlen($substring);
         if ($substringlength != 9) {
             $info['error'][] = 'Failed to read 7 bytes at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (only read ' . $substringlength . ' bytes)';
             return false;
         }
         // this would be easier with 64-bit math, but split it up to allow for 32-bit:
         $header1 = Helper::BigEndian2Int(substr($substring, 0, 2));
         $header2 = Helper::BigEndian2Int(substr($substring, 2, 4));
         $header3 = Helper::BigEndian2Int(substr($substring, 6, 1));
         $info['aac']['header']['raw']['syncword'] = ($header1 & 0xfff0) >> 4;
         if ($info['aac']['header']['raw']['syncword'] != 0xfff) {
             $info['error'][] = 'Synch pattern (0x0FFF) not found at offset ' . (ftell($this->getid3->fp) - $substringlength) . ' (found 0x0' . strtoupper(dechex($info['aac']['header']['raw']['syncword'])) . ' instead)';
             //if ($info['fileformat'] == 'aac') {
             //	return true;
             //}
             unset($info['aac']);
             return false;
         }
         // Gather info for first frame only - this takes time to do 1000 times!
         if ($framenumber == 0) {
             $info['aac']['header_type'] = 'ADTS';
             $info['fileformat'] = 'aac';
             $info['audio']['dataformat'] = 'aac';
             $info['aac']['header']['raw']['mpeg_version'] = ($header1 & 0x8) >> 3;
             $info['aac']['header']['raw']['mpeg_layer'] = ($header1 & 0x6) >> 1;
             $info['aac']['header']['raw']['protection_absent'] = ($header1 & 0x1) >> 0;
             $info['aac']['header']['raw']['profile_code'] = ($header2 & 0xc0000000) >> 30;
             $info['aac']['header']['raw']['sample_rate_code'] = ($header2 & 0x3c000000) >> 26;
             $info['aac']['header']['raw']['private_stream'] = ($header2 & 0x2000000) >> 25;
             $info['aac']['header']['raw']['channels_code'] = ($header2 & 0x1c00000) >> 22;
             $info['aac']['header']['raw']['original'] = ($header2 & 0x200000) >> 21;
             $info['aac']['header']['raw']['home'] = ($header2 & 0x100000) >> 20;
             $info['aac']['header']['raw']['copyright_stream'] = ($header2 & 0x80000) >> 19;
             $info['aac']['header']['raw']['copyright_start'] = ($header2 & 0x40000) >> 18;
             $info['aac']['header']['raw']['frame_length'] = ($header2 & 0x3ffe0) >> 5;
             $info['aac']['header']['mpeg_version'] = $info['aac']['header']['raw']['mpeg_version'] ? 2 : 4;
             $info['aac']['header']['crc_present'] = $info['aac']['header']['raw']['protection_absent'] ? false : true;
             $info['aac']['header']['profile'] = self::AACprofileLookup($info['aac']['header']['raw']['profile_code'], $info['aac']['header']['mpeg_version']);
             $info['aac']['header']['sample_frequency'] = self::AACsampleRateLookup($info['aac']['header']['raw']['sample_rate_code']);
             $info['aac']['header']['private'] = (bool) $info['aac']['header']['raw']['private_stream'];
             $info['aac']['header']['original'] = (bool) $info['aac']['header']['raw']['original'];
             $info['aac']['header']['home'] = (bool) $info['aac']['header']['raw']['home'];
             $info['aac']['header']['channels'] = $info['aac']['header']['raw']['channels_code'] == 7 ? 8 : $info['aac']['header']['raw']['channels_code'];
             if ($ReturnExtendedInfo) {
                 $info['aac'][$framenumber]['copyright_id_bit'] = (bool) $info['aac']['header']['raw']['copyright_stream'];
                 $info['aac'][$framenumber]['copyright_id_start'] = (bool) $info['aac']['header']['raw']['copyright_start'];
             }
             if ($info['aac']['header']['raw']['mpeg_layer'] != 0) {
                 $info['warning'][] = 'Layer error - expected "0", found "' . $info['aac']['header']['raw']['mpeg_layer'] . '" instead';
             }
             if ($info['aac']['header']['sample_frequency'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: sample_frequency == zero';
                 return false;
             }
             $info['audio']['sample_rate'] = $info['aac']['header']['sample_frequency'];
             $info['audio']['channels'] = $info['aac']['header']['channels'];
         }
         $FrameLength = ($header2 & 0x3ffe0) >> 5;
         if (!isset($BitrateCache[$FrameLength])) {
             $BitrateCache[$FrameLength] = $info['aac']['header']['sample_frequency'] / 1024 * $FrameLength * 8;
         }
         Helper::safe_inc($info['aac']['bitrate_distribution'][$BitrateCache[$FrameLength]], 1);
         $info['aac'][$framenumber]['aac_frame_length'] = $FrameLength;
         $info['aac'][$framenumber]['adts_buffer_fullness'] = ($header2 & 0x1f) << 6 & ($header3 & 0xfc) >> 2;
         if ($info['aac'][$framenumber]['adts_buffer_fullness'] == 0x7ff) {
             $info['audio']['bitrate_mode'] = 'vbr';
         } else {
             $info['audio']['bitrate_mode'] = 'cbr';
         }
         $info['aac'][$framenumber]['num_raw_data_blocks'] = ($header3 & 0x3) >> 0;
         if ($info['aac']['header']['crc_present']) {
             //$info['aac'][$framenumber]['crc'] = GetId3_lib::BigEndian2Int(substr($substring, 7, 2);
         }
         if (!$ReturnExtendedInfo) {
             unset($info['aac'][$framenumber]);
         }
         /*
         $rounded_precision = 5000;
         $info['aac']['bitrate_distribution_rounded'] = array();
         foreach ($info['aac']['bitrate_distribution'] as $bitrate => $count) {
             $rounded_bitrate = round($bitrate / $rounded_precision) * $rounded_precision;
             getid3_lib::safe_inc($info['aac']['bitrate_distribution_rounded'][$rounded_bitrate], $count);
         }
         ksort($info['aac']['bitrate_distribution_rounded']);
         */
         $byteoffset += $FrameLength;
         if (++$framenumber < $MaxFramesToScan && $byteoffset + 10 < $info['avdataend']) {
             // keep scanning
         } else {
             $info['aac']['frames'] = $framenumber;
             $info['playtime_seconds'] = $info['avdataend'] / $byteoffset * ($framenumber * 1024 / $info['aac']['header']['sample_frequency']);
             // (1 / % of file scanned) * (samples / (samples/sec)) = seconds
             if ($info['playtime_seconds'] == 0) {
                 $info['error'][] = 'Corrupt AAC file: playtime_seconds == zero';
                 return false;
             }
             $info['audio']['bitrate'] = ($info['avdataend'] - $info['avdataoffset']) * 8 / $info['playtime_seconds'];
             ksort($info['aac']['bitrate_distribution']);
             $info['audio']['encoder_options'] = $info['aac']['header_type'] . ' ' . $info['aac']['header']['profile'];
             return true;
         }
     }
     // should never get here.
 }