32 |
32 |
33 $initialOffset = $info['avdataoffset']; |
33 $initialOffset = $info['avdataoffset']; |
34 |
34 |
35 if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) { |
35 if (!$this->getOnlyMPEGaudioInfo($info['avdataoffset'])) { |
36 if ($this->allow_bruteforce) { |
36 if ($this->allow_bruteforce) { |
37 $info['error'][] = 'Rescanning file in BruteForce mode'; |
37 $this->error('Rescanning file in BruteForce mode'); |
38 $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info); |
38 $this->getOnlyMPEGaudioInfoBruteForce($this->getid3->fp, $info); |
39 } |
39 } |
40 } |
40 } |
41 |
41 |
42 |
42 |
422 $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray(); |
422 $MPEGaudioModeExtensionLookup = self::MPEGaudioModeExtensionArray(); |
423 $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray(); |
423 $MPEGaudioEmphasisLookup = self::MPEGaudioEmphasisArray(); |
424 } |
424 } |
425 |
425 |
426 if ($this->fseek($offset) != 0) { |
426 if ($this->fseek($offset) != 0) { |
427 $info['error'][] = 'decodeMPEGaudioHeader() failed to seek to next offset at '.$offset; |
427 $this->error('decodeMPEGaudioHeader() failed to seek to next offset at '.$offset); |
428 return false; |
428 return false; |
429 } |
429 } |
430 //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame |
430 //$headerstring = $this->fread(1441); // worst-case max length = 32kHz @ 320kbps layer 3 = 1441 bytes/frame |
431 $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data |
431 $headerstring = $this->fread(226); // LAME header at offset 36 + 190 bytes of Xing/LAME data |
432 |
432 |
435 // where $aa..$aa is the four-byte mpeg-audio header (below) |
435 // where $aa..$aa is the four-byte mpeg-audio header (below) |
436 // $bb $bb is the optional 2-byte CRC |
436 // $bb $bb is the optional 2-byte CRC |
437 // and $cc... is the audio data |
437 // and $cc... is the audio data |
438 |
438 |
439 $head4 = substr($headerstring, 0, 4); |
439 $head4 = substr($headerstring, 0, 4); |
440 |
440 $head4_key = getid3_lib::PrintHexBytes($head4, true, false, false); |
441 static $MPEGaudioHeaderDecodeCache = array(); |
441 static $MPEGaudioHeaderDecodeCache = array(); |
442 if (isset($MPEGaudioHeaderDecodeCache[$head4])) { |
442 if (isset($MPEGaudioHeaderDecodeCache[$head4_key])) { |
443 $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4]; |
443 $MPEGheaderRawArray = $MPEGaudioHeaderDecodeCache[$head4_key]; |
444 } else { |
444 } else { |
445 $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4); |
445 $MPEGheaderRawArray = self::MPEGaudioHeaderDecode($head4); |
446 $MPEGaudioHeaderDecodeCache[$head4] = $MPEGheaderRawArray; |
446 $MPEGaudioHeaderDecodeCache[$head4_key] = $MPEGheaderRawArray; |
447 } |
447 } |
448 |
448 |
449 static $MPEGaudioHeaderValidCache = array(); |
449 static $MPEGaudioHeaderValidCache = array(); |
450 if (!isset($MPEGaudioHeaderValidCache[$head4])) { // Not in cache |
450 if (!isset($MPEGaudioHeaderValidCache[$head4_key])) { // Not in cache |
451 //$MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) |
451 //$MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, true); // allow badly-formatted freeformat (from LAME 3.90 - 3.93.1) |
452 $MPEGaudioHeaderValidCache[$head4] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); |
452 $MPEGaudioHeaderValidCache[$head4_key] = self::MPEGaudioHeaderValid($MPEGheaderRawArray, false, false); |
453 } |
453 } |
454 |
454 |
455 // shortcut |
455 // shortcut |
456 if (!isset($info['mpeg']['audio'])) { |
456 if (!isset($info['mpeg']['audio'])) { |
457 $info['mpeg']['audio'] = array(); |
457 $info['mpeg']['audio'] = array(); |
458 } |
458 } |
459 $thisfile_mpeg_audio = &$info['mpeg']['audio']; |
459 $thisfile_mpeg_audio = &$info['mpeg']['audio']; |
460 |
460 |
461 |
461 |
462 if ($MPEGaudioHeaderValidCache[$head4]) { |
462 if ($MPEGaudioHeaderValidCache[$head4_key]) { |
463 $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; |
463 $thisfile_mpeg_audio['raw'] = $MPEGheaderRawArray; |
464 } else { |
464 } else { |
465 $info['error'][] = 'Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset; |
465 $this->error('Invalid MPEG audio header ('.getid3_lib::PrintHexBytes($head4).') at offset '.$offset); |
466 return false; |
466 return false; |
467 } |
467 } |
468 |
468 |
469 if (!$FastMPEGheaderScan) { |
469 if (!$FastMPEGheaderScan) { |
470 $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']]; |
470 $thisfile_mpeg_audio['version'] = $MPEGaudioVersionLookup[$thisfile_mpeg_audio['raw']['version']]; |
488 } |
488 } |
489 } |
489 } |
490 |
490 |
491 if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { |
491 if ($thisfile_mpeg_audio['raw']['bitrate'] == 15) { |
492 // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 |
492 // http://www.hydrogenaudio.org/?act=ST&f=16&t=9682&st=0 |
493 $info['warning'][] = 'Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'; |
493 $this->warning('Invalid bitrate index (15), this is a known bug in free-format MP3s encoded by LAME v3.90 - 3.93.1'); |
494 $thisfile_mpeg_audio['raw']['bitrate'] = 0; |
494 $thisfile_mpeg_audio['raw']['bitrate'] = 0; |
495 } |
495 } |
496 $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; |
496 $thisfile_mpeg_audio['padding'] = (bool) $thisfile_mpeg_audio['raw']['padding']; |
497 $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; |
497 $thisfile_mpeg_audio['bitrate'] = $MPEGaudioBitrateLookup[$thisfile_mpeg_audio['version']][$thisfile_mpeg_audio['layer']][$thisfile_mpeg_audio['raw']['bitrate']]; |
498 |
498 |
510 |
510 |
511 case 'mono': |
511 case 'mono': |
512 if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { |
512 if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] <= 192000)) { |
513 // these are ok |
513 // these are ok |
514 } else { |
514 } else { |
515 $info['error'][] = $thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; |
515 $this->error($thisfile_mpeg_audio['bitrate'].'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'); |
516 return false; |
516 return false; |
517 } |
517 } |
518 break; |
518 break; |
519 |
519 |
520 case 'stereo': |
520 case 'stereo': |
521 case 'joint stereo': |
521 case 'joint stereo': |
522 case 'dual channel': |
522 case 'dual channel': |
523 if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { |
523 if (($thisfile_mpeg_audio['bitrate'] == 'free') || ($thisfile_mpeg_audio['bitrate'] == 64000) || ($thisfile_mpeg_audio['bitrate'] >= 96000)) { |
524 // these are ok |
524 // these are ok |
525 } else { |
525 } else { |
526 $info['error'][] = intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'; |
526 $this->error(intval(round($thisfile_mpeg_audio['bitrate'] / 1000)).'kbps not allowed in Layer 2, '.$thisfile_mpeg_audio['channelmode'].'.'); |
527 return false; |
527 return false; |
528 } |
528 } |
529 break; |
529 break; |
530 |
530 |
531 } |
531 } |
646 if ($thisfile_mpeg_audio['xing_flags']['bytes']) { |
646 if ($thisfile_mpeg_audio['xing_flags']['bytes']) { |
647 $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); |
647 $thisfile_mpeg_audio['VBR_bytes'] = getid3_lib::BigEndian2Int(substr($headerstring, $VBRidOffset + 12, 4)); |
648 } |
648 } |
649 |
649 |
650 //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { |
650 //if (($thisfile_mpeg_audio['bitrate'] == 'free') && !empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { |
651 if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { |
651 //if (!empty($thisfile_mpeg_audio['VBR_frames']) && !empty($thisfile_mpeg_audio['VBR_bytes'])) { |
652 |
652 if (!empty($thisfile_mpeg_audio['VBR_frames'])) { |
653 $framelengthfloat = $thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']; |
653 $used_filesize = 0; |
|
654 if (!empty($thisfile_mpeg_audio['VBR_bytes'])) { |
|
655 $used_filesize = $thisfile_mpeg_audio['VBR_bytes']; |
|
656 } elseif (!empty($info['filesize'])) { |
|
657 $used_filesize = $info['filesize']; |
|
658 $used_filesize -= intval(@$info['id3v2']['headerlength']); |
|
659 $used_filesize -= (isset($info['id3v1']) ? 128 : 0); |
|
660 $used_filesize -= (isset($info['tag_offset_end']) ? $info['tag_offset_end'] - $info['tag_offset_start'] : 0); |
|
661 $this->warning('MP3.Xing header missing VBR_bytes, assuming MPEG audio portion of file is '.number_format($used_filesize).' bytes'); |
|
662 } |
|
663 |
|
664 $framelengthfloat = $used_filesize / $thisfile_mpeg_audio['VBR_frames']; |
654 |
665 |
655 if ($thisfile_mpeg_audio['layer'] == '1') { |
666 if ($thisfile_mpeg_audio['layer'] == '1') { |
656 // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 |
667 // BitRate = (((FrameLengthInBytes / 4) - Padding) * SampleRate) / 12 |
657 //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; |
668 //$info['audio']['bitrate'] = ((($framelengthfloat / 4) - intval($thisfile_mpeg_audio['padding'])) * $thisfile_mpeg_audio['sample_rate']) / 12; |
658 $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12; |
669 $info['audio']['bitrate'] = ($framelengthfloat / 4) * $thisfile_mpeg_audio['sample_rate'] * (2 / $info['audio']['channels']) / 12; |
835 $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); |
846 $thisfile_mpeg_audio_lame_raw['surround_info'] = ($PresetSurroundBytes & 0x3800); |
836 $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); |
847 $thisfile_mpeg_audio_lame['surround_info'] = self::LAMEsurroundInfoLookup($thisfile_mpeg_audio_lame_raw['surround_info']); |
837 $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); |
848 $thisfile_mpeg_audio_lame['preset_used_id'] = ($PresetSurroundBytes & 0x07FF); |
838 $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); |
849 $thisfile_mpeg_audio_lame['preset_used'] = self::LAMEpresetUsedLookup($thisfile_mpeg_audio_lame); |
839 if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { |
850 if (!empty($thisfile_mpeg_audio_lame['preset_used_id']) && empty($thisfile_mpeg_audio_lame['preset_used'])) { |
840 $info['warning'][] = 'Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'; |
851 $this->warning('Unknown LAME preset used ('.$thisfile_mpeg_audio_lame['preset_used_id'].') - please report to info@getid3.org'); |
841 } |
852 } |
842 if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { |
853 if (($thisfile_mpeg_audio_lame['short_version'] == 'LAME3.90.') && !empty($thisfile_mpeg_audio_lame['preset_used_id'])) { |
843 // this may change if 3.90.4 ever comes out |
854 // this may change if 3.90.4 ever comes out |
844 $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3'; |
855 $thisfile_mpeg_audio_lame['short_version'] = 'LAME3.90.3'; |
845 } |
856 } |
879 if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) { |
890 if ($this->RecursiveFrameScanning($offset, $nextframetestoffset, true)) { |
880 $recursivesearch = false; |
891 $recursivesearch = false; |
881 $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; |
892 $thisfile_mpeg_audio['bitrate_mode'] = 'cbr'; |
882 } |
893 } |
883 if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { |
894 if ($thisfile_mpeg_audio['bitrate_mode'] == 'vbr') { |
884 $info['warning'][] = 'VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'; |
895 $this->warning('VBR file with no VBR header. Bitrate values calculated from actual frame bitrates.'); |
885 } |
896 } |
886 } |
897 } |
887 |
898 |
888 } |
899 } |
889 |
900 |
906 // $this->fseek($info['avdataend']); |
917 // $this->fseek($info['avdataend']); |
907 // $PossibleNullByte = $this->fread(1); |
918 // $PossibleNullByte = $this->fread(1); |
908 // $this->fseek($prenullbytefileoffset); |
919 // $this->fseek($prenullbytefileoffset); |
909 // if ($PossibleNullByte === "\x00") { |
920 // if ($PossibleNullByte === "\x00") { |
910 $info['avdataend']--; |
921 $info['avdataend']--; |
911 // $info['warning'][] = 'Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'; |
922 // $this->warning('Extra null byte at end of MP3 data assumed to be RIFF padding and therefore ignored'); |
912 // } else { |
923 // } else { |
913 // $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; |
924 // $this->warning('Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'); |
914 // } |
925 // } |
915 } else { |
926 } else { |
916 $info['warning'][] = 'Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'; |
927 $this->warning('Too much data in file: expecting '.$ExpectedNumberOfAudioBytes.' bytes of audio data, found '.($info['avdataend'] - $info['avdataoffset']).' ('.(($info['avdataend'] - $info['avdataoffset']) - $ExpectedNumberOfAudioBytes).' bytes too many)'); |
917 } |
928 } |
918 } |
929 } |
919 } |
930 } |
920 |
931 |
921 if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) { |
932 if (($thisfile_mpeg_audio['bitrate'] == 'free') && empty($info['audio']['bitrate'])) { |
946 } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { |
957 } elseif ((($thisfile_mpeg_audio['version'] == '2') || ($thisfile_mpeg_audio['version'] == '2.5')) && ($thisfile_mpeg_audio['layer'] == 3)) { |
947 $bytes_per_frame = 576; |
958 $bytes_per_frame = 576; |
948 } |
959 } |
949 $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0); |
960 $thisfile_mpeg_audio['VBR_bitrate'] = (isset($thisfile_mpeg_audio['VBR_bytes']) ? (($thisfile_mpeg_audio['VBR_bytes'] / $thisfile_mpeg_audio['VBR_frames']) * 8) * ($info['audio']['sample_rate'] / $bytes_per_frame) : 0); |
950 if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { |
961 if ($thisfile_mpeg_audio['VBR_bitrate'] > 0) { |
951 $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; |
962 $info['audio']['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; |
952 $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion |
963 $thisfile_mpeg_audio['bitrate'] = $thisfile_mpeg_audio['VBR_bitrate']; // to avoid confusion |
953 } |
964 } |
954 break; |
965 break; |
955 } |
966 } |
956 } |
967 } |
1072 return true; |
1083 return true; |
1073 } |
1084 } |
1074 |
1085 |
1075 public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) { |
1086 public function RecursiveFrameScanning(&$offset, &$nextframetestoffset, $ScanAsCBR) { |
1076 $info = &$this->getid3->info; |
1087 $info = &$this->getid3->info; |
1077 $firstframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); |
1088 $firstframetestarray = array('error' => array(), 'warning'=> array(), 'avdataend' => $info['avdataend'], 'avdataoffset' => $info['avdataoffset']); |
1078 $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false); |
1089 $this->decodeMPEGaudioHeader($offset, $firstframetestarray, false); |
1079 |
1090 |
1080 for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) { |
1091 for ($i = 0; $i < GETID3_MP3_VALID_CHECK_FRAMES; $i++) { |
1081 // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch |
1092 // check next GETID3_MP3_VALID_CHECK_FRAMES frames for validity, to make sure we haven't run across a false synch |
1082 if (($nextframetestoffset + 4) >= $info['avdataend']) { |
1093 if (($nextframetestoffset + 4) >= $info['avdataend']) { |
1083 // end of file |
1094 // end of file |
1084 return true; |
1095 return true; |
1085 } |
1096 } |
1086 |
1097 |
1087 $nextframetestarray = array('error'=>'', 'warning'=>'', 'avdataend'=>$info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); |
1098 $nextframetestarray = array('error' => array(), 'warning' => array(), 'avdataend' => $info['avdataend'], 'avdataoffset'=>$info['avdataoffset']); |
1088 if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) { |
1099 if ($this->decodeMPEGaudioHeader($nextframetestoffset, $nextframetestarray, false)) { |
1089 if ($ScanAsCBR) { |
1100 if ($ScanAsCBR) { |
1090 // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header |
1101 // force CBR mode, used for trying to pick out invalid audio streams with valid(?) VBR headers, or VBR streams with no VBR header |
1091 if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) { |
1102 if (!isset($nextframetestarray['mpeg']['audio']['bitrate']) || !isset($firstframetestarray['mpeg']['audio']['bitrate']) || ($nextframetestarray['mpeg']['audio']['bitrate'] != $firstframetestarray['mpeg']['audio']['bitrate'])) { |
1092 return false; |
1103 return false; |
1096 |
1107 |
1097 // next frame is OK, get ready to check the one after that |
1108 // next frame is OK, get ready to check the one after that |
1098 if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { |
1109 if (isset($nextframetestarray['mpeg']['audio']['framelength']) && ($nextframetestarray['mpeg']['audio']['framelength'] > 0)) { |
1099 $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; |
1110 $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength']; |
1100 } else { |
1111 } else { |
1101 $info['error'][] = 'Frame at offset ('.$offset.') is has an invalid frame length.'; |
1112 $this->error('Frame at offset ('.$offset.') is has an invalid frame length.'); |
1102 return false; |
1113 return false; |
1103 } |
1114 } |
1104 |
1115 |
1105 } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) { |
1116 } elseif (!empty($firstframetestarray['mpeg']['audio']['framelength']) && (($nextframetestoffset + $firstframetestarray['mpeg']['audio']['framelength']) > $info['avdataend'])) { |
1106 |
1117 |
1151 } |
1162 } |
1152 if (($framelength2 > 4) && ($framelength2 < $framelength1)) { |
1163 if (($framelength2 > 4) && ($framelength2 < $framelength1)) { |
1153 $framelength = $framelength2; |
1164 $framelength = $framelength2; |
1154 } |
1165 } |
1155 if (!$framelength) { |
1166 if (!$framelength) { |
1156 $info['error'][] = 'Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset; |
1167 $this->error('Cannot find next free-format synch pattern ('.getid3_lib::PrintHexBytes($SyncPattern1).' or '.getid3_lib::PrintHexBytes($SyncPattern2).') after offset '.$offset); |
1157 return false; |
1168 return false; |
1158 } else { |
1169 } else { |
1159 $info['warning'][] = 'ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'; |
1170 $this->warning('ModeExtension varies between first frame and other frames (known free-format issue in LAME 3.88)'); |
1160 $info['audio']['codec'] = 'LAME'; |
1171 $info['audio']['codec'] = 'LAME'; |
1161 $info['audio']['encoder'] = 'LAME3.88'; |
1172 $info['audio']['encoder'] = 'LAME3.88'; |
1162 $SyncPattern1 = substr($SyncPattern1, 0, 3); |
1173 $SyncPattern1 = substr($SyncPattern1, 0, 3); |
1163 $SyncPattern2 = substr($SyncPattern2, 0, 3); |
1174 $SyncPattern2 = substr($SyncPattern2, 0, 3); |
1164 } |
1175 } |
1181 } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) { |
1192 } elseif ((substr($NextSyncPattern, 2, strlen($SyncPattern1)) == $SyncPattern1) || (substr($NextSyncPattern, 2, strlen($SyncPattern2)) == $SyncPattern2)) { |
1182 // ok - found one byte later than expected (last frame was padded, first frame wasn't) |
1193 // ok - found one byte later than expected (last frame was padded, first frame wasn't) |
1183 $ActualFrameLengthValues[] = ($framelength + 1); |
1194 $ActualFrameLengthValues[] = ($framelength + 1); |
1184 $nextoffset++; |
1195 $nextoffset++; |
1185 } else { |
1196 } else { |
1186 $info['error'][] = 'Did not find expected free-format sync pattern at offset '.$nextoffset; |
1197 $this->error('Did not find expected free-format sync pattern at offset '.$nextoffset); |
1187 return false; |
1198 return false; |
1188 } |
1199 } |
1189 $nextoffset += $framelength; |
1200 $nextoffset += $framelength; |
1190 } |
1201 } |
1191 if (count($ActualFrameLengthValues) > 0) { |
1202 if (count($ActualFrameLengthValues) > 0) { |
1279 getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]); |
1290 getid3_lib::safe_inc($Distribution['version'][$LongMPEGversionLookup[$head4]]); |
1280 getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]); |
1291 getid3_lib::safe_inc($Distribution['padding'][intval($LongMPEGpaddingLookup[$head4])]); |
1281 getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]); |
1292 getid3_lib::safe_inc($Distribution['frequency'][$LongMPEGfrequencyLookup[$head4]]); |
1282 if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) { |
1293 if ($max_frames_scan && (++$frames_scanned >= $max_frames_scan)) { |
1283 $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']); |
1294 $pct_data_scanned = ($this->ftell() - $info['avdataoffset']) / ($info['avdataend'] - $info['avdataoffset']); |
1284 $info['warning'][] = 'too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'; |
1295 $this->warning('too many MPEG audio frames to scan, only scanned first '.$max_frames_scan.' frames ('.number_format($pct_data_scanned * 100, 1).'% of file) and extrapolated distribution, playtime and bitrate may be incorrect.'); |
1285 foreach ($Distribution as $key1 => $value1) { |
1296 foreach ($Distribution as $key1 => $value1) { |
1286 foreach ($value1 as $key2 => $value2) { |
1297 foreach ($value1 as $key2 => $value2) { |
1287 $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned); |
1298 $Distribution[$key1][$key2] = round($value2 / $pct_data_scanned); |
1288 } |
1299 } |
1289 } |
1300 } |
1306 $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; |
1317 $info['mpeg']['audio']['frequency_distribution'] = $Distribution['frequency']; |
1307 $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; |
1318 $info['mpeg']['audio']['layer_distribution'] = $Distribution['layer']; |
1308 $info['mpeg']['audio']['version_distribution'] = $Distribution['version']; |
1319 $info['mpeg']['audio']['version_distribution'] = $Distribution['version']; |
1309 $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; |
1320 $info['mpeg']['audio']['padding_distribution'] = $Distribution['padding']; |
1310 if (count($Distribution['version']) > 1) { |
1321 if (count($Distribution['version']) > 1) { |
1311 $info['error'][] = 'Corrupt file - more than one MPEG version detected'; |
1322 $this->error('Corrupt file - more than one MPEG version detected'); |
1312 } |
1323 } |
1313 if (count($Distribution['layer']) > 1) { |
1324 if (count($Distribution['layer']) > 1) { |
1314 $info['error'][] = 'Corrupt file - more than one MPEG layer detected'; |
1325 $this->error('Corrupt file - more than one MPEG layer detected'); |
1315 } |
1326 } |
1316 if (count($Distribution['frequency']) > 1) { |
1327 if (count($Distribution['frequency']) > 1) { |
1317 $info['error'][] = 'Corrupt file - more than one MPEG sample rate detected'; |
1328 $this->error('Corrupt file - more than one MPEG sample rate detected'); |
1318 } |
1329 } |
1319 |
1330 |
1320 |
1331 |
1321 $bittotal = 0; |
1332 $bittotal = 0; |
1322 foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) { |
1333 foreach ($Distribution['bitrate'] as $bitratevalue => $bitratecount) { |
1324 $bittotal += ($bitratevalue * $bitratecount); |
1335 $bittotal += ($bitratevalue * $bitratecount); |
1325 } |
1336 } |
1326 } |
1337 } |
1327 $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); |
1338 $info['mpeg']['audio']['frame_count'] = array_sum($Distribution['bitrate']); |
1328 if ($info['mpeg']['audio']['frame_count'] == 0) { |
1339 if ($info['mpeg']['audio']['frame_count'] == 0) { |
1329 $info['error'][] = 'no MPEG audio frames found'; |
1340 $this->error('no MPEG audio frames found'); |
1330 return false; |
1341 return false; |
1331 } |
1342 } |
1332 $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']); |
1343 $info['mpeg']['audio']['bitrate'] = ($bittotal / $info['mpeg']['audio']['frame_count']); |
1333 $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); |
1344 $info['mpeg']['audio']['bitrate_mode'] = ((count($Distribution['bitrate']) > 0) ? 'vbr' : 'cbr'); |
1334 $info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); |
1345 $info['mpeg']['audio']['sample_rate'] = getid3_lib::array_max($Distribution['frequency'], true); |
1359 } |
1370 } |
1360 |
1371 |
1361 $this->fseek($avdataoffset); |
1372 $this->fseek($avdataoffset); |
1362 $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset); |
1373 $sync_seek_buffer_size = min(128 * 1024, $info['avdataend'] - $avdataoffset); |
1363 if ($sync_seek_buffer_size <= 0) { |
1374 if ($sync_seek_buffer_size <= 0) { |
1364 $info['error'][] = 'Invalid $sync_seek_buffer_size at offset '.$avdataoffset; |
1375 $this->error('Invalid $sync_seek_buffer_size at offset '.$avdataoffset); |
1365 return false; |
1376 return false; |
1366 } |
1377 } |
1367 $header = $this->fread($sync_seek_buffer_size); |
1378 $header = $this->fread($sync_seek_buffer_size); |
1368 $sync_seek_buffer_size = strlen($header); |
1379 $sync_seek_buffer_size = strlen($header); |
1369 $SynchSeekOffset = 0; |
1380 $SynchSeekOffset = 0; |
1370 while ($SynchSeekOffset < $sync_seek_buffer_size) { |
1381 while ($SynchSeekOffset < $sync_seek_buffer_size) { |
1371 if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) { |
1382 if ((($avdataoffset + $SynchSeekOffset) < $info['avdataend']) && !feof($this->getid3->fp)) { |
1372 |
1383 |
1373 if ($SynchSeekOffset > $sync_seek_buffer_size) { |
1384 if ($SynchSeekOffset > $sync_seek_buffer_size) { |
1374 // if a synch's not found within the first 128k bytes, then give up |
1385 // if a synch's not found within the first 128k bytes, then give up |
1375 $info['error'][] = 'Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'; |
1386 $this->error('Could not find valid MPEG audio synch within the first '.round($sync_seek_buffer_size / 1024).'kB'); |
1376 if (isset($info['audio']['bitrate'])) { |
1387 if (isset($info['audio']['bitrate'])) { |
1377 unset($info['audio']['bitrate']); |
1388 unset($info['audio']['bitrate']); |
1378 } |
1389 } |
1379 if (isset($info['mpeg']['audio'])) { |
1390 if (isset($info['mpeg']['audio'])) { |
1380 unset($info['mpeg']['audio']); |
1391 unset($info['mpeg']['audio']); |
1384 } |
1395 } |
1385 return false; |
1396 return false; |
1386 |
1397 |
1387 } elseif (feof($this->getid3->fp)) { |
1398 } elseif (feof($this->getid3->fp)) { |
1388 |
1399 |
1389 $info['error'][] = 'Could not find valid MPEG audio synch before end of file'; |
1400 $this->error('Could not find valid MPEG audio synch before end of file'); |
1390 if (isset($info['audio']['bitrate'])) { |
1401 if (isset($info['audio']['bitrate'])) { |
1391 unset($info['audio']['bitrate']); |
1402 unset($info['audio']['bitrate']); |
1392 } |
1403 } |
1393 if (isset($info['mpeg']['audio'])) { |
1404 if (isset($info['mpeg']['audio'])) { |
1394 unset($info['mpeg']['audio']); |
1405 unset($info['mpeg']['audio']); |
1442 $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; |
1453 $GarbageOffsetStart = $FirstFrameAVDataOffset + $FirstFrameThisfileInfo['mpeg']['audio']['framelength']; |
1443 $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; |
1454 $GarbageOffsetEnd = $avdataoffset + $SynchSeekOffset; |
1444 if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) { |
1455 if ($this->decodeMPEGaudioHeader($GarbageOffsetEnd, $dummy, true, true)) { |
1445 $info = $dummy; |
1456 $info = $dummy; |
1446 $info['avdataoffset'] = $GarbageOffsetEnd; |
1457 $info['avdataoffset'] = $GarbageOffsetEnd; |
1447 $info['warning'][] = 'apparently-valid VBR header not used because could not find '.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; |
1458 $this->warning('apparently-valid VBR header not used because could not find '.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); |
1448 } else { |
1459 } else { |
1449 $info['warning'][] = 'using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'; |
1460 $this->warning('using data from VBR header even though could not find '.GETID3_MP3_VALID_CHECK_FRAMES.' consecutive MPEG-audio frames immediately after VBR header (garbage data for '.($GarbageOffsetEnd - $GarbageOffsetStart).' bytes between '.$GarbageOffsetStart.' and '.$GarbageOffsetEnd.')'); |
1450 } |
1461 } |
1451 } |
1462 } |
1452 } |
1463 } |
1453 if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) { |
1464 if (isset($info['mpeg']['audio']['bitrate_mode']) && ($info['mpeg']['audio']['bitrate_mode'] == 'vbr') && !isset($info['mpeg']['audio']['VBR_method'])) { |
1454 // VBR file with no VBR header |
1465 // VBR file with no VBR header |
1537 } |
1548 } |
1538 } |
1549 } |
1539 } |
1550 } |
1540 } |
1551 } |
1541 if ($pct_data_scanned > 0) { |
1552 if ($pct_data_scanned > 0) { |
1542 $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.'; |
1553 $this->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.'); |
1543 foreach ($info['mpeg']['audio'] as $key1 => $value1) { |
1554 foreach ($info['mpeg']['audio'] as $key1 => $value1) { |
1544 if (!preg_match('#_distribution$#i', $key1)) { |
1555 if (!preg_match('#_distribution$#i', $key1)) { |
1545 continue; |
1556 continue; |
1546 } |
1557 } |
1547 foreach ($value1 as $key2 => $value2) { |
1558 foreach ($value1 as $key2 => $value2) { |
1562 if ($bitratevalue != 'free') { |
1573 if ($bitratevalue != 'free') { |
1563 $bittotal += ($bitratevalue * $bitratecount); |
1574 $bittotal += ($bitratevalue * $bitratecount); |
1564 } |
1575 } |
1565 } |
1576 } |
1566 if ($framecounter == 0) { |
1577 if ($framecounter == 0) { |
1567 $info['error'][] = 'Corrupt MP3 file: framecounter == zero'; |
1578 $this->error('Corrupt MP3 file: framecounter == zero'); |
1568 return false; |
1579 return false; |
1569 } |
1580 } |
1570 $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter); |
1581 $info['mpeg']['audio']['frame_count'] = getid3_lib::CastAsInt($framecounter); |
1571 $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); |
1582 $info['mpeg']['audio']['bitrate'] = ($bittotal / $framecounter); |
1572 |
1583 |
1597 if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) { |
1608 if (($avdataoffset + $SynchSeekOffset) >= $info['avdataend']) { |
1598 // end of file/data |
1609 // end of file/data |
1599 |
1610 |
1600 if (empty($info['mpeg']['audio'])) { |
1611 if (empty($info['mpeg']['audio'])) { |
1601 |
1612 |
1602 $info['error'][] = 'could not find valid MPEG synch before end of file'; |
1613 $this->error('could not find valid MPEG synch before end of file'); |
1603 if (isset($info['audio']['bitrate'])) { |
1614 if (isset($info['audio']['bitrate'])) { |
1604 unset($info['audio']['bitrate']); |
1615 unset($info['audio']['bitrate']); |
1605 } |
1616 } |
1606 if (isset($info['mpeg']['audio'])) { |
1617 if (isset($info['mpeg']['audio'])) { |
1607 unset($info['mpeg']['audio']); |
1618 unset($info['mpeg']['audio']); |