You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

PHP获取WAV与MP3文件时长问题求助:MP3无返回结果

解决WAV/MP3时长获取问题:MP3无返回的修复方案

你的问题很明确:当前函数只针对WAV格式做了处理,完全没考虑MP3的解析逻辑,所以调用它处理MP3时自然没有返回结果。我帮你修改并扩展了函数,同时提供两种方案供你选择:原生PHP解析,或者用更可靠的第三方库。

方案一:原生PHP扩展函数(支持WAV+MP3)

这个方案不需要额外依赖,直接扩展你原有的函数,添加MP3解析逻辑:

function audioDur($file) {
    $fp = fopen($file, 'r');
    if (!$fp) {
        return ''; // 文件打开失败返回空值
    }

    // 处理WAV文件(保留你原有的正确逻辑)
    $header = fread($fp, 4);
    if ($header == 'RIFF') {
        fseek($fp, 20);
        $rawheader = fread($fp, 16);
        $header = unpack('vtype/vchannels/Vsamplerate/Vbytespersec/valignment/vbits', $rawheader);
        $pos = ftell($fp);
        while (fread($fp,4) != 'data' && !feof($fp)) {
            $pos++;
            fseek($fp, $pos);
        }
        $rawheader = fread($fp, 4);
        $data = unpack('Vdatasize', $rawheader);
        $sec = $data['datasize'] / $header['bytespersec'];
        $minutes = intval(($sec / 60) % 60);
        $seconds = intval($sec % 60);
        fclose($fp);
        return str_pad($minutes, 2, '0', STR_PAD_LEFT) . ':' . str_pad($seconds, 2, '0', STR_PAD_LEFT);
    } 
    // 新增:处理MP3文件
    elseif ($header == 'ID3' || strpos($header, "\xFF") !== false) {
        // 跳过ID3标签(如果存在)
        if ($header == 'ID3') {
            fseek($fp, 6);
            $rawSize = fread($fp, 4);
            // ID3标签大小是同步安全整数,需要转换
            $size = (ord($rawSize[0]) & 0x7F) << 21 | (ord($rawSize[1]) & 0x7F) << 14 | (ord($rawSize[2]) & 0x7F) << 7 | ord($rawSize[3]) & 0x7F;
            fseek($fp, 10 + $size);
        }

        $frames = 0;
        $samplerate = 0;

        while (!feof($fp)) {
            $byte = fread($fp, 1);
            // 寻找MP3帧头标识(0xFF开头,且后续字节符合帧头规则)
            if (ord($byte) == 0xFF) {
                $nextByte = fread($fp, 1);
                if ((ord($nextByte) & 0xE0) == 0xE0) {
                    $frameHeader = $byte . $nextByte . fread($fp, 2);
                    $version = (ord($frameHeader[1]) & 0x18) >> 3;
                    $samplerateIndex = (ord($frameHeader[2]) & 0x0C) >> 2;

                    // 根据MP3版本获取采样率
                    $samplerates = [
                        [44100, 48000, 32000], // MPEG 2.5
                        [0, 0, 0],             // 保留值
                        [22050, 24000, 16000], // MPEG 2
                        [44100, 48000, 32000]  // MPEG 1
                    ];
                    $versionIndex = $version == 0 ? 0 : ($version == 2 ? 2 : 3);
                    $samplerate = $samplerates[$versionIndex][$samplerateIndex];
                    if ($samplerate == 0) continue;

                    // 计算当前帧长度,跳过帧内容
                    $bitrateIndex = (ord($frameHeader[2]) & 0xF0) >> 4;
                    $bitrates = [
                        [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320], // MPEG1
                        [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160]    // MPEG2/2.5
                    ];
                    $bitrateTableIndex = $versionIndex == 3 ? 0 : 1;
                    $bitrate = $bitrates[$bitrateTableIndex][$bitrateIndex] * 1000;
                    if ($bitrate == 0) continue;

                    $padding = (ord($frameHeader[2]) & 0x02) >> 1;
                    $frameLength = (144 * $bitrate) / $samplerate + $padding;
                    $frames++;

                    // 跳过当前帧剩余内容
                    fseek($fp, ftell($fp) + $frameLength - 4);
                }
            }
        }

        // 计算总时长
        if ($frames > 0 && $samplerate > 0) {
            $samplesPerFrame = 1152; // MP3 Layer III每帧采样数
            $totalSamples = $frames * $samplesPerFrame;
            $sec = $totalSamples / $samplerate;
            $minutes = intval(($sec / 60) % 60);
            $seconds = intval($sec % 60);
            fclose($fp);
            return str_pad($minutes, 2, '0', STR_PAD_LEFT) . ':' . str_pad($seconds, 2, '0', STR_PAD_LEFT);
        } else {
            fclose($fp);
            return '';
        }
    } else {
        fclose($fp);
        return ''; // 不支持的格式
    }
}

// 调用方式(和你原来的逻辑一致)
$file = "your-audio-file.mp3"; // 替换成你的文件路径
if (preg_match('/[.](wav|WAV|mp3|MP3)$/', $file)) {
    $dur = audioDur($file);
} else {
    $dur = '';
}

echo $dur;

关键修改点:

  • 函数重命名为audioDur,明确支持两种格式
  • 新增MP3解析逻辑:处理ID3标签、识别MP3帧头、计算总帧数和时长
  • 增加了文件打开失败的容错处理
  • 完善了文件资源释放,避免内存泄漏

方案二:使用getID3库(更可靠的生产环境方案)

原生解析MP3容易遇到边缘情况(比如VBR编码、特殊标签的MP3),如果你的项目需要更高的兼容性,推荐使用getID3这个专门处理音频元数据的PHP库:

  1. 先下载getID3库文件,解压到你的项目目录
  2. 使用以下代码:
require_once('getid3/getid3.php'); // 替换成你的getID3路径

function audioDur($file) {
    $getID3 = new getID3();
    $audioInfo = $getID3->analyze($file);
    
    // 检查是否成功获取时长
    if (isset($audioInfo['playtime_seconds'])) {
        $totalSeconds = $audioInfo['playtime_seconds'];
        $minutes = intval(($totalSeconds / 60) % 60);
        $seconds = intval($totalSeconds % 60);
        return str_pad($minutes, 2, '0', STR_PAD_LEFT) . ':' . str_pad($seconds, 2, '0', STR_PAD_LEFT);
    }
    
    return ''; // 无法获取时长时返回空
}

// 调用方式不变
$file = "your-audio-file.mp3";
if (preg_match('/[.](wav|WAV|mp3|MP3)$/', $file)) {
    $dur = audioDur($file);
} else {
    $dur = '';
}

echo $dur;

优势:

  • 无需自己处理复杂的格式解析逻辑
  • 支持几乎所有常见音频格式(不只是WAV和MP3)
  • 兼容性更好,能处理各种特殊编码的音频文件

内容的提问来源于stack exchange,提问作者medk

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.13 09:22:51