Esempio n. 1
0
if (isset($_POST['Analyze']) && $_POST['HeaderHexBytes']) {
    $headerbytearray = explode(' ', $_POST['HeaderHexBytes']);
    if (count($headerbytearray) != 4) {
        die('Invalid byte pattern');
    }
    $headerstring = '';
    foreach ($headerbytearray as $textbyte) {
        $headerstring .= chr(hexdec($textbyte));
    }
    $MP3fileInfo['error'] = '';
    $MPEGheaderRawArray = MPEGaudioHeaderDecode(substr($headerstring, 0, 4));
    if (MPEGaudioHeaderValid($MPEGheaderRawArray, true)) {
        $MP3fileInfo['raw'] = $MPEGheaderRawArray;
        $MP3fileInfo['version'] = MPEGaudioVersionLookup($MP3fileInfo['raw']['version']);
        $MP3fileInfo['layer'] = MPEGaudioLayerLookup($MP3fileInfo['raw']['layer']);
        $MP3fileInfo['protection'] = MPEGaudioCRCLookup($MP3fileInfo['raw']['protection']);
        $MP3fileInfo['bitrate'] = MPEGaudioBitrateLookup($MP3fileInfo['version'], $MP3fileInfo['layer'], $MP3fileInfo['raw']['bitrate']);
        $MP3fileInfo['frequency'] = MPEGaudioFrequencyLookup($MP3fileInfo['version'], $MP3fileInfo['raw']['sample_rate']);
        $MP3fileInfo['padding'] = (bool) $MP3fileInfo['raw']['padding'];
        $MP3fileInfo['private'] = (bool) $MP3fileInfo['raw']['private'];
        $MP3fileInfo['channelmode'] = MPEGaudioChannelModeLookup($MP3fileInfo['raw']['channelmode']);
        $MP3fileInfo['channels'] = $MP3fileInfo['channelmode'] == 'mono' ? 1 : 2;
        $MP3fileInfo['modeextension'] = MPEGaudioModeExtensionLookup($MP3fileInfo['layer'], $MP3fileInfo['raw']['modeextension']);
        $MP3fileInfo['copyright'] = (bool) $MP3fileInfo['raw']['copyright'];
        $MP3fileInfo['original'] = (bool) $MP3fileInfo['raw']['original'];
        $MP3fileInfo['emphasis'] = MPEGaudioEmphasisLookup($MP3fileInfo['raw']['emphasis']);
        if ($MP3fileInfo['protection']) {
            $MP3fileInfo['crc'] = BigEndian2Int(substr($headerstring, 4, 2));
        }
        if ($MP3fileInfo['frequency'] > 0) {
            $MP3fileInfo['framelength'] = MPEGaudioFrameLength($MP3fileInfo['bitrate'], $MP3fileInfo['version'], $MP3fileInfo['layer'], (int) $MP3fileInfo['padding'], $MP3fileInfo['frequency']);
function decodeMPEGaudioHeader($fd, $offset, &$MP3fileInfo, $recursivesearch = TRUE)
{
    if ($offset >= $MP3fileInfo['filesize']) {
        $MP3fileInfo['error'] .= "\n" . 'end of file encounter looking for MPEG synch';
        return FALSE;
    }
    fseek($fd, $offset, SEEK_SET);
    $headerstring = fread($fd, FREAD_BUFFER_SIZE);
    // MP3 audio frame structure:
    // $aa $aa $aa $aa [$bb $bb] $cc...
    // where $aa..$aa is the four-byte mpeg-audio header (below)
    // $bb $bb is the optional 2-byte CRC
    // and $cc... is the audio data
    // AAAAAAAA AAABBCCD EEEEFFGH IIJJKLMM
    // 01234567 01234567 01234567 01234567
    // 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
    $byte1 = BigEndian2Bin(substr($headerstring, 0, 1));
    $byte2 = BigEndian2Bin(substr($headerstring, 1, 1));
    $byte3 = BigEndian2Bin(substr($headerstring, 2, 1));
    $byte4 = BigEndian2Bin(substr($headerstring, 3, 1));
    if (substr(BigEndian2Bin(substr($headerstring, 0, 2)), 0, 11) == '11111111111') {
        // synch detected (11 set bits in a row)
    } else {
        $MP3fileInfo['error'] .= "\n" . 'MPEG-audio synch not found where expected (at offset ' . $offset . ')';
        return FALSE;
    }
    $MP3fileInfo['mpeg']['audio']['raw']['version'] = bindec(substr($byte2, 3, 2));
    $MP3fileInfo['mpeg']['audio']['raw']['layer'] = bindec(substr($byte2, 5, 2));
    $MP3fileInfo['mpeg']['audio']['raw']['protection'] = bindec(substr($byte2, 7, 1));
    $MP3fileInfo['mpeg']['audio']['raw']['bitrate'] = bindec(substr($byte3, 0, 4));
    $MP3fileInfo['mpeg']['audio']['raw']['frequency'] = bindec(substr($byte3, 4, 2));
    $MP3fileInfo['mpeg']['audio']['raw']['padding'] = bindec(substr($byte3, 6, 1));
    $MP3fileInfo['mpeg']['audio']['raw']['private'] = bindec(substr($byte3, 7, 1));
    $MP3fileInfo['mpeg']['audio']['raw']['channelmode'] = bindec(substr($byte4, 0, 2));
    $MP3fileInfo['mpeg']['audio']['raw']['modeextension'] = bindec(substr($byte4, 2, 2));
    $MP3fileInfo['mpeg']['audio']['raw']['copyright'] = bindec(substr($byte4, 4, 1));
    $MP3fileInfo['mpeg']['audio']['raw']['original'] = bindec(substr($byte4, 5, 1));
    $MP3fileInfo['mpeg']['audio']['raw']['emphasis'] = bindec(substr($byte4, 6, 2));
    if (!MPEGaudioHeaderValid($MP3fileInfo['mpeg']['audio']['raw'])) {
        $MP3fileInfo['error'] .= "\n" . 'invalid MPEG audio header at offset ' . $offset;
        return FALSE;
    }
    $MP3fileInfo['mpeg']['audio']['version'] = MPEGaudioVersionLookup($MP3fileInfo['mpeg']['audio']['raw']['version']);
    $MP3fileInfo['mpeg']['audio']['layer'] = MPEGaudioLayerLookup($MP3fileInfo['mpeg']['audio']['raw']['layer']);
    $MP3fileInfo['mpeg']['audio']['protection'] = MPEGaudioCRCLookup($MP3fileInfo['mpeg']['audio']['raw']['protection']);
    $MP3fileInfo['mpeg']['audio']['bitrate'] = MPEGaudioBitrateLookup($MP3fileInfo['mpeg']['audio']['version'], $MP3fileInfo['mpeg']['audio']['layer'], $MP3fileInfo['mpeg']['audio']['raw']['bitrate']);
    $MP3fileInfo['mpeg']['audio']['frequency'] = MPEGaudioFrequencyLookup($MP3fileInfo['mpeg']['audio']['version'], $MP3fileInfo['mpeg']['audio']['raw']['frequency']);
    $MP3fileInfo['mpeg']['audio']['padding'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['padding'];
    $MP3fileInfo['mpeg']['audio']['private'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['private'];
    $MP3fileInfo['mpeg']['audio']['channelmode'] = MPEGaudioChannelModeLookup($MP3fileInfo['mpeg']['audio']['raw']['channelmode']);
    $MP3fileInfo['mpeg']['audio']['channels'] = $MP3fileInfo['mpeg']['audio']['channelmode'] == 'mono' ? 1 : 2;
    $MP3fileInfo['mpeg']['audio']['modeextension'] = MPEGaudioModeExtensionLookup($MP3fileInfo['mpeg']['audio']['layer'], $MP3fileInfo['mpeg']['audio']['raw']['modeextension']);
    $MP3fileInfo['mpeg']['audio']['copyright'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['copyright'];
    $MP3fileInfo['mpeg']['audio']['original'] = (bool) $MP3fileInfo['mpeg']['audio']['raw']['original'];
    $MP3fileInfo['mpeg']['audio']['emphasis'] = MPEGaudioEmphasisLookup($MP3fileInfo['mpeg']['audio']['raw']['emphasis']);
    if ($MP3fileInfo['mpeg']['audio']['protection']) {
        $MP3fileInfo['mpeg']['audio']['crc'] = BigEndian2Int(substr($headerstring, 4, 2));
    }
    if ($MP3fileInfo['mpeg']['audio']['bitrate'] != 'free') {
        if ($MP3fileInfo['mpeg']['audio']['version'] == '1') {
            if ($MP3fileInfo['mpeg']['audio']['layer'] == 'I') {
                $FrameLengthCoefficient = 48;
                $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 4 : 0;
                // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long."
            } else {
                // Layer II / III
                $FrameLengthCoefficient = 144;
                $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 1 : 0;
                // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long."
            }
        } else {
            // MPEG-2 / MPEG-2.5
            if ($MP3fileInfo['mpeg']['audio']['layer'] == 'I') {
                $FrameLengthCoefficient = 24;
                $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 4 : 0;
                // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long."
            } else {
                // Layer II / III
                $FrameLengthCoefficient = 72;
                $FrameLengthPadding = $MP3fileInfo['mpeg']['audio']['padding'] ? 1 : 0;
                // "For Layer I slot is 32 bits long, for Layer II and Layer III slot is 8 bits long."
            }
        }
        // FrameLengthInBytes = ((Coefficient * BitRate) / SampleRate) + Padding
        // http://66.96.216.160/cgi-bin/YaBB.pl?board=c&action=display&num=1018474068
        // -> "Finding the next frame synch" on www.r3mix.net forums if the above link goes dead
        $MP3fileInfo['mpeg']['audio']['framelength'] = (int) floor($FrameLengthCoefficient * 1000 * $MP3fileInfo['mpeg']['audio']['bitrate'] / $MP3fileInfo['mpeg']['audio']['frequency']) + $FrameLengthPadding;
    }
    $MP3fileInfo['bitrate'] = 1000 * $MP3fileInfo['mpeg']['audio']['bitrate'];
    $nextframetestarray = array('error' => '', 'filesize' => $MP3fileInfo['filesize']);
    if (isset($MP3fileInfo['mpeg']['audio']['framelength'])) {
        $nextframetestoffset = $offset + $MP3fileInfo['mpeg']['audio']['framelength'];
    } else {
        $nextframetestoffset = $MP3fileInfo['filesize'];
    }
    if ($recursivesearch && isset($MP3fileInfo['mpeg']['audio']['framelength']) && $MP3fileInfo['mpeg']['audio']['framelength']) {
        for ($i = 0; $i < 5; $i++) {
            // check next 5 frames for validity, to make sure we haven't run across a false synch
            if ($nextframetestoffset >= $MP3fileInfo['filesize']) {
                // end of file
                break;
            }
            if (decodeMPEGaudioHeader($fd, $nextframetestoffset, $nextframetestarray, FALSE)) {
                // next frame is OK, get ready to check the one after that
                $nextframetestoffset += $nextframetestarray['mpeg']['audio']['framelength'];
            } else {
                // next frame is not valid, note the error and fail, so scanning can contiue for a valid frame sequence
                $MP3fileInfo['error'] .= "\n" . 'Frame at offset(' . $offset . ') is valid, but the next one at (' . $nextframetestoffset . ') is not.';
                return FALSE;
            }
        }
    }
    // For Layer II there are some combinations of bitrate and mode which are not allowed.
    if ($MP3fileInfo['mpeg']['audio']['layer'] == 'II') {
        switch ($MP3fileInfo['mpeg']['audio']['channelmode']) {
            case 'mono':
                if ($MP3fileInfo['mpeg']['audio']['bitrate'] == 'free' || $MP3fileInfo['mpeg']['audio']['bitrate'] <= 192) {
                    // these are ok
                } else {
                    $MP3fileInfo['error'] .= "\n" . $MP3fileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $MP3fileInfo['mpeg']['audio']['channelmode'] . '.';
                }
                break;
            case 'stereo':
            case 'joint stereo':
            case 'dual channel':
                if ($MP3fileInfo['mpeg']['audio']['bitrate'] == 'free' || $MP3fileInfo['mpeg']['audio']['bitrate'] == 64 || $MP3fileInfo['mpeg']['audio']['bitrate'] >= 96) {
                    // these are ok
                } else {
                    $MP3fileInfo['error'] .= "\n" . $MP3fileInfo['mpeg']['audio']['bitrate'] . 'kbps not allowed in Layer II, ' . $MP3fileInfo['mpeg']['audio']['channelmode'] . '.';
                }
                break;
        }
    }
    ////////////////////////////////////////////////////////////////////////////////////
    // Variable-bitrate headers
    if ($MP3fileInfo['mpeg']['audio']['version'] == '1') {
        if ($MP3fileInfo['mpeg']['audio']['channelmode'] == 'mono') {
            $VBRidOffset = 17 + 4;
            // 21 bytes
        } else {
            $VBRidOffset = 32 + 4;
            // 36 bytes
        }
    } else {
        // 2 or 2.5
        if ($MP3fileInfo['mpeg']['audio']['channelmode'] == 'mono') {
            $VBRidOffset = 9 + 4;
            // 13 bytes
        } else {
            $VBRidOffset = 17 + 4;
            // 21 bytes
        }
    }
    $VBRid = substr($headerstring, $VBRidOffset, 4);
    if ($VBRid == 'Xing') {
        $MP3fileInfo['mpeg']['audio']['bitratemode'] = 'VBR';
        $MP3fileInfo['mpeg']['audio']['VBR_method'] = 'Xing';
    } else {
        if ($VBRid == 'VBRI') {
            $MP3fileInfo['mpeg']['audio']['bitratemode'] = 'VBR';
            $MP3fileInfo['mpeg']['audio']['VBR_method'] = 'Fraunhofer';
        } else {
            $MP3fileInfo['mpeg']['audio']['bitratemode'] = 'CBR';
        }
    }
    if ($MP3fileInfo['mpeg']['audio']['bitratemode'] == 'VBR') {
        if ($MP3fileInfo['mpeg']['audio']['VBR_method'] == 'Xing') {
            $XingVBROffset = $VBRidOffset + 4;
            $XingHeader_Flags = substr($headerstring, $XingVBROffset, 4);
            $XingVBROffset += 4;
            $XingHeader_byte4 = BigEndian2Bin(substr($XingHeader_Flags, 3, 1));
            $XingHeader_flags['frames'] = substr($XingHeader_byte4, 4, 1);
            $XingHeader_flags['bytes'] = substr($XingHeader_byte4, 5, 1);
            $XingHeader_flags['toc'] = substr($XingHeader_byte4, 6, 1);
            $XingHeader_flags['vbr_scale'] = substr($XingHeader_byte4, 7, 1);
            if ($XingHeader_flags['frames'] == '1') {
                $XingHeader_Frames = substr($headerstring, $XingVBROffset, 4);
                $XingVBROffset += 4;
                $MP3fileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int($XingHeader_Frames);
            }
            if ($XingHeader_flags['bytes'] == '1') {
                $XingHeader_Bytes = substr($headerstring, $XingVBROffset, 4);
                $XingVBROffset += 4;
                $MP3fileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int($XingHeader_Bytes);
            }
        } else {
            if ($MP3fileInfo['mpeg']['audio']['VBR_method'] == 'Fraunhofer') {
                // specs taken from http://minnie.tuhs.org/pipermail/mp3encoder/2001-January/001800.html
                $FraunhoferVBROffset = $VBRidOffset + 4;
                $Fraunhofer_version = substr($headerstring, $FraunhoferVBROffset, 4);
                $FraunhoferVBROffset += 4;
                $Fraunhofer_quality = substr($headerstring, $FraunhoferVBROffset, 2);
                $FraunhoferVBROffset += 2;
                $MP3fileInfo['mpeg']['audio']['VBR_quality'] = BigEndian2Int($Fraunhofer_quality);
                $Fraunhofer_Bytes = substr($headerstring, $FraunhoferVBROffset, 4);
                $FraunhoferVBROffset += 4;
                $MP3fileInfo['mpeg']['audio']['VBR_bytes'] = BigEndian2Int($Fraunhofer_Bytes);
                $Fraunhofer_Frames = substr($headerstring, $FraunhoferVBROffset, 4);
                $FraunhoferVBROffset += 4;
                $MP3fileInfo['mpeg']['audio']['VBR_frames'] = BigEndian2Int($Fraunhofer_Frames);
            }
        }
        $MP3fileInfo['mpeg']['audio']['VBR_frames']--;
        // don't count the Xing / VBRI frame
        if ($MP3fileInfo['mpeg']['audio']['version'] == '1' && $MP3fileInfo['mpeg']['audio']['layer'] == 'I') {
            $MP3fileInfo['mpeg']['audio']['VBR_bitrate'] = $MP3fileInfo['mpeg']['audio']['VBR_bytes'] / $MP3fileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($MP3fileInfo['mpeg']['audio']['frequency'] / 384) / 1000;
        } else {
            if (($MP3fileInfo['mpeg']['audio']['version'] == '2' || $MP3fileInfo['mpeg']['audio']['version'] == '2.5') && $MP3fileInfo['mpeg']['audio']['layer'] == 'III') {
                $MP3fileInfo['mpeg']['audio']['VBR_bitrate'] = $MP3fileInfo['mpeg']['audio']['VBR_bytes'] / $MP3fileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($MP3fileInfo['mpeg']['audio']['frequency'] / 576) / 1000;
            } else {
                $MP3fileInfo['mpeg']['audio']['VBR_bitrate'] = $MP3fileInfo['mpeg']['audio']['VBR_bytes'] / $MP3fileInfo['mpeg']['audio']['VBR_frames'] * 8 * ($MP3fileInfo['mpeg']['audio']['frequency'] / 1152) / 1000;
            }
        }
        if ($MP3fileInfo['mpeg']['audio']['VBR_bitrate'] > 0) {
            $MP3fileInfo['bitrate'] = 1000 * $MP3fileInfo['mpeg']['audio']['VBR_bitrate'];
            unset($MP3fileInfo['mpeg']['audio']['bitrate']);
            // to avoid confusion
        }
    }
    return TRUE;
}