Created
April 16, 2012 17:22
-
-
Save zonble/2400085 to your computer and use it in GitHub Desktop.
PHP MP3 Parser
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/* | |
* A tiny parser which parses ID3 tags and MP3 frames. | |
*/ | |
function parseMP3($mp3file) { | |
function _parseID3TagsV2($data) { | |
define("HEADER_LENGTH", 10); | |
define("HEADER_BODY_LENGTH_INFO_OFFSET", 6); | |
define("HEADER_BODY_LENGTH_INFO_LENGTH", 4); | |
define("FRAME_HEADER_LENGTH", 10); | |
define("FRAME_ID_LENGTH", 4); | |
define("FRAME_BODY_LENGTH_INFO_LENGTH", 4); | |
function MASK($bits) { | |
return (1 << ($bits)) - 1; | |
} | |
function _readUInt28($bytes) { | |
$BITSUSED = 7; $val = 0; | |
foreach(str_split($bytes) as $byte) { | |
$val = ($val << $BITSUSED) | (ord($byte) & MASK($BITSUSED)); | |
} | |
return min($val, MASK($BITSUSED * 4)); | |
} | |
$tags = array(); | |
$ID3TagHeader = substr($data, 0, HEADER_LENGTH); | |
$ID3TagBodyInfo = substr($data, HEADER_BODY_LENGTH_INFO_OFFSET, HEADER_BODY_LENGTH_INFO_LENGTH); | |
$ID3TagBodyLength = _readUInt28($ID3TagBodyInfo); | |
$readHead = HEADER_LENGTH; | |
while ($readHead < $ID3TagBodyLength) { | |
$frameHeader = substr($data, $readHead, FRAME_HEADER_LENGTH); | |
$frameID = substr($frameHeader, 0, FRAME_ID_LENGTH); | |
$frameBodyLengthInfo = substr($frameHeader, FRAME_ID_LENGTH, FRAME_BODY_LENGTH_INFO_LENGTH); | |
$frameBodyLength = _readUInt28($frameBodyLengthInfo); | |
$frameBody = substr($data, $readHead + FRAME_HEADER_LENGTH, $frameBodyLength); | |
$textEncodingType = intval($frameBody[0]); | |
$frameContent = substr($frameBody, 1, strlen($frameBody) -1); | |
$tags[$frameID] = $frameContent; | |
$readHead += $FRAME_HEADER_LENGTH + $frameBodyLength; | |
} | |
return array($tags, $readHead); | |
} | |
function _parseMP3Data($data, $size, $inReadHead=0) { | |
// $data = str_split($data); | |
$readHead = $inReadHead; | |
$foundFirstFrame = False; | |
$MP3BitrateLookup = array(0, 32000, 40000, 48000, 56000, 64000, 80000, 96000, 112000, 128000, 160000, 192000, 224000, 256000, 320000, 0); | |
$parsedFrameCount = 0; | |
$skippedByteCount = 0; | |
while(True) { | |
$remainingLength = $size - $readHead; | |
if ($remainingLength < 2) { | |
break; | |
} | |
$frameSync = (ord($data[$readHead]) << 8) | (ord($data[$readHead + 1]) & (0x80 | 0x40 | 0x20)); | |
if ($frameSync != 0xffe0) { | |
print "skip $readHead\n"; | |
$readHead += 1; | |
$skippedByteCount += 1; | |
continue; | |
} | |
if ($remainingLength < 6) { | |
break; | |
} | |
$audioVersion = (ord($data[$readHead + 1]) >> 3) & 0x03; | |
$layer = (ord($data[$readHead + 1]) >> 1) & 0x03; | |
$hasCRC = !(ord($data[$readHead + 1]) & 0x01); | |
$bitrateIndex = ord($data[$readHead + 2]) >> 4; | |
$sampleRateIndex = ord($data[$readHead + 2]) >> 2 & 0x03; | |
$bitrate = $MP3BitrateLookup[$bitrateIndex]; | |
// print "audioVersion:$audioVersion, layer:$layer, sampleRateIndex:$sampleRateIndex, bitrate:$bitrate\n"; | |
if (!($audioVersion == 0x03 and $layer == 0x01 and $sampleRateIndex == 0x00)) { | |
// Test | |
} | |
$hasPadding = (!(!((ord($data[$readHead + 2]) >> 1) & 0x01))); | |
$padding = 0; if ($hasPadding) { $padding = 1; }; | |
$CRCPadding = 0; if ($hasCRC) { $CRCPadding = 2; }; | |
$frameLength = intval((144 * $bitrate / 44100)) + $padding + $CRCPadding; | |
$readHead += $frameLength; | |
if ($remainingLength < $frameLength) { | |
break; | |
} | |
$parsedFrameCount++; | |
} | |
print "parsedFrameCount:".$parsedFrameCount."\n"; | |
print "skippedByteCount:".$skippedByteCount."\n"; | |
return True; | |
} | |
$handle = fopen($mp3file, "rb"); | |
$size = filesize($mp3file); | |
$data = fread($handle, $size); | |
$readHead = 0; | |
$tags = NULL; | |
if ($data[0] == 'I' && $data[1] == 'D' && $data[2] == '3') { | |
$result = _parseID3TagsV2($data); | |
$tags = $result[0]; | |
$readHead = $result[1]; | |
} | |
return array(_parseMP3Data($data, $size, 0), $tags); | |
} | |
?> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
zonble 大
line 42 should be:
$readHead += FRAME_HEADER_LENGTH + $frameBodyLength;
you have an additional '$' for FRAME_HEADER_LENGTH