本文目录导读:

在PHP项目中处理音频文件转码,最常用的方案是通过 FFmpeg 进行操作,下面详细介绍几种常见的实现方法。
安装 FFmpeg
Linux (Ubuntu/Debian)
sudo apt update sudo apt install ffmpeg
macOS
brew install ffmpeg
Windows
从官网下载:https://ffmpeg.org/download.html
PHP中使用 exec() 调用 FFmpeg
基本转换示例
<?php
class AudioConverter {
private $ffmpegPath;
public function __construct($ffmpegPath = 'ffmpeg') {
$this->ffmpegPath = $ffmpegPath;
}
/**
* 转换音频格式
* @param string $inputFile 输入文件路径
* @param string $outputFile 输出文件路径
* @param array $options 额外参数
* @return array
*/
public function convert($inputFile, $outputFile, $options = []) {
// 构建命令
$cmd = $this->ffmpegPath . ' -i ' . escapeshellarg($inputFile);
// 添加选项
if (!empty($options)) {
foreach ($options as $key => $value) {
$cmd .= " -{$key} {$value}";
}
}
$cmd .= ' ' . escapeshellarg($outputFile);
$cmd .= ' 2>&1'; // 错误输出重定向
// 执行命令
exec($cmd, $output, $returnCode);
return [
'success' => $returnCode === 0,
'output' => $output,
'command' => $cmd
];
}
}
常用的音频格式转换场景
MP3 转 WAV
$converter = new AudioConverter();
$result = $converter->convert('input.mp3', 'output.wav', [
'acodec' => 'pcm_s16le',
'ar' => '44100',
'ac' => '2'
]);
WAV 转 MP3
$result = $converter->convert('input.wav', 'output.mp3', [
'codec:a' => 'libmp3lame',
'b:a' => '192k',
'ar' => '44100',
'ac' => '2'
]);
其他格式转换
// OGG 转 MP3
$result = $converter->convert('input.ogg', 'output.mp3', [
'codec:a' => 'libmp3lame',
'q:a' => '2' // 质量 0-9,2 为高质量
]);
// FLAC 转 AAC
$result = $converter->convert('input.flac', 'output.aac', [
'codec:a' => 'aac',
'b:a' => '192k'
]);
// 任意格式转 WAV(常用于语音识别)
$result = $converter->convert('input.any', 'output.wav', [
'acodec' => 'pcm_s16le',
'ar' => '16000', // 16kHz 采样率
'ac' => '1', // 单声道
'sample_fmt' => 's16'
]);
使用 PHP-FFMpeg 库(推荐)
如果你更喜欢面向对象的接口,可以使用 php-ffmpeg 库:
安装
composer require php-ffmpeg/php-ffmpeg
使用示例
<?php
require_once 'vendor/autoload.php';
use FFMpeg\FFMpeg;
use FFMpeg\Format\Audio\Mp3;
use FFMpeg\Format\Audio\Wav;
use FFMpeg\Format\Audio\Aac;
class AudioProcessor {
private $ffmpeg;
public function __construct() {
$this->ffmpeg = FFMpeg::create([
'ffmpeg.binaries' => '/usr/bin/ffmpeg',
'ffprobe.binaries' => '/usr/bin/ffprobe',
'timeout' => 3600,
'ffmpeg.threads' => 12,
]);
}
/**
* 转换音频格式
*/
public function convertToMp3($inputFile, $outputFile) {
$audio = $this->ffmpeg->open($inputFile);
$format = new Mp3();
$format->setAudioKiloBitrate(192)
->setAudioChannels(2)
->setAudioCodec('libmp3lame');
$audio->save($format, $outputFile);
return true;
}
/**
* 提取音频片段
*/
public function extractSegment($inputFile, $outputFile, $start, $duration) {
$audio = $this->ffmpeg->open($inputFile);
$format = new Mp3();
$audio->filters()->clip(\FFMpeg\Coordinate\TimeCode::fromSeconds($start),
\FFMpeg\Coordinate\TimeCode::fromSeconds($duration));
$audio->save($format, $outputFile);
return true;
}
}
完整的上传转码示例
<?php
class AudioUploadHandler {
private $uploadDir;
private $ffmpeg;
public function __construct($uploadDir = 'uploads/') {
$this->uploadDir = $uploadDir;
$this->ffmpeg = new AudioConverter();
// 确保目录存在
if (!is_dir($uploadDir)) {
mkdir($uploadDir, 0755, true);
}
}
/**
* 上传并转码音频文件
*/
public function uploadAndConvert($file, $targetFormat = 'mp3') {
// 验证文件
$this->validateFile($file);
// 生成唯一文件名
$extension = pathinfo($file['name'], PATHINFO_EXTENSION);
$filename = uniqid() . '.' . $extension;
$inputPath = $this->uploadDir . $filename;
// 上传文件
if (!move_uploaded_file($file['tmp_name'], $inputPath)) {
throw new Exception("文件上传失败");
}
// 生成输出路径
$outputFilename = uniqid() . '.' . $targetFormat;
$outputPath = $this->uploadDir . $outputFilename;
// 根据目标格式设置参数
$options = $this->getFormatOptions($targetFormat);
// 执行转码
$result = $this->ffmpeg->convert($inputPath, $outputPath, $options);
if ($result['success']) {
// 删除原始文件
unlink($inputPath);
return [
'success' => true,
'file' => $outputPath,
'filename' => $outputFilename,
'format' => $targetFormat
];
} else {
// 清理失败的文件
unlink($inputPath);
if (file_exists($outputPath)) {
unlink($outputPath);
}
throw new Exception("转码失败: " . implode("\n", $result['output']));
}
}
/**
* 获取格式参数
*/
private function getFormatOptions($format) {
$options = [
'mp3' => [
'codec:a' => 'libmp3lame',
'b:a' => '192k',
'ar' => '44100',
'ac' => '2'
],
'wav' => [
'acodec' => 'pcm_s16le',
'ar' => '44100',
'ac' => '2'
],
'aac' => [
'codec:a' => 'aac',
'b:a' => '128k'
],
'ogg' => [
'codec:a' => 'libvorbis',
'q:a' => '3'
]
];
return $options[$format] ?? [];
}
/**
* 验证上传的文件
*/
private function validateFile($file) {
$allowedTypes = ['audio/mpeg', 'audio/wav', 'audio/ogg', 'audio/x-wav', 'audio/mp4'];
if ($file['error'] !== UPLOAD_ERR_OK) {
throw new Exception("上传错误");
}
if (!in_array($file['type'], $allowedTypes)) {
throw new Exception("不支持的音频格式");
}
if ($file['size'] > 100 * 1024 * 1024) { // 100MB
throw new Exception("文件过大");
}
}
}
// 使用示例
try {
$handler = new AudioUploadHandler();
$result = $handler->uploadAndConvert($_FILES['audio'], 'mp3');
echo "成功: " . $result['filename'];
} catch (Exception $e) {
echo "错误: " . $e->getMessage();
}
安全注意事项
// 1. 始终对输入进行转义
$cmd = 'ffmpeg -i ' . escapeshellarg($inputFile) . ' ' . escapeshellarg($outputFile);
// 2. 设置执行超时
set_time_limit(300); // 5分钟超时
// 3. 限制资源使用
$cmd = 'nice -n 19 ffmpeg -i input.mp3 -threads 2 output.mp3';
// 4. 验证输出文件
if (file_exists($outputFile) && filesize($outputFile) > 0) {
// 文件生成成功
}
// 5. 清理临时文件
register_shutdown_function(function() use ($tempFile) {
if (file_exists($tempFile)) {
unlink($tempFile);
}
});
异步处理建议
对于大文件,建议使用消息队列异步处理:
// 使用 Redis 或 RabbitMQ
$job = [
'input' => '/path/to/input.mp3',
'output' => '/path/to/output.wav',
'format' => 'wav',
'options' => ['ar' => 16000, 'ac' => 1]
];
// 将任务添加到队列
$queue->push('audio_convert', json_encode($job));
// 在 Worker 进程中处理
// worker.php
while ($job = $queue->pop('audio_convert')) {
$data = json_decode($job, true);
convertAudio($data['input'], $data['output'], $data['options']);
// 更新数据库状态
$db->update('files', ['status' => 'completed'], ['id' => $data['id']]);
}
这个方案涵盖了大部分的音频转码需求,你可以根据具体项目需求进行调整和扩展。