本文目录导读:

在PHP项目中使用FFmpeg,主要有两种方式:直接通过命令行执行 和 使用PHP扩展库,下面详细介绍这两种方法。
环境准备
安装FFmpeg
# Ubuntu/Debian sudo apt-get install ffmpeg # CentOS/RHEL sudo yum install ffmpeg # macOS brew install ffmpeg # Windows: 从官网下载并配置环境变量
验证安装
ffmpeg -version which ffmpeg # 查看路径
方法一:直接执行shell命令(推荐)
这是最常用且灵活的方法,通过exec()、shell_exec()或proc_open()等PHP函数执行ffmpeg命令。
基础示例
<?php
// 视频转码
$input = '/path/to/input.mp4';
$output = '/path/to/output.mp4';
$cmd = "ffmpeg -i {$input} -c:v libx264 -c:a aac {$output} 2>&1";
exec($cmd, $output, $return_var);
if ($return_var === 0) {
echo "转换成功";
} else {
echo "转换失败: " . implode("\n", $output);
}
封装成函数
<?php
class FFmpegCommand {
private $ffmpegPath = 'ffmpeg';
private $ffprobePath = 'ffprobe';
public function __construct($ffmpegPath = null) {
if ($ffmpegPath) {
$this->ffmpegPath = $ffmpegPath;
}
}
/**
* 执行ffmpeg命令
*/
public function execute($command) {
$fullCommand = "{$this->ffmpegPath} {$command} 2>&1";
$output = [];
$returnVar = 0;
exec($fullCommand, $output, $returnVar);
return [
'success' => $returnVar === 0,
'output' => implode("\n", $output)
];
}
/**
* 视频转码
*/
public function transcode($input, $output, $options = []) {
$defaultOptions = '-c:v libx264 -c:a aac -preset medium';
$cmd = "-i {$input} {$defaultOptions} {$output} -y";
return $this->execute($cmd);
}
/**
* 截取视频缩略图
*/
public function thumbnail($videoPath, $output, $time = '00:00:01') {
$cmd = "-i {$videoPath} -ss {$time} -vframes 1 {$output} -y";
return $this->execute($cmd);
}
/**
* 合并视频
*/
public function mergeVideos($fileList, $output) {
$listFile = tempnam(sys_get_temp_dir(), 'ffmpeg_');
file_put_contents($listFile, $fileList);
$cmd = "-f concat -safe 0 -i {$listFile} -c copy {$output} -y";
$result = $this->execute($cmd);
unlink($listFile);
return $result;
}
/**
* 获取视频信息
*/
public function getVideoInfo($videoPath) {
$cmd = "-v quiet -print_format json -show_format -show_streams {$videoPath}";
$result = $this->execute($cmd);
if ($result['success']) {
return json_decode($result['output'], true);
}
return null;
}
}
// 使用示例
$ffmpeg = new FFmpegCommand();
$info = $ffmpeg->getVideoInfo('input.mp4');
echo "时长: " . $info['format']['duration'] . "秒";
方法二:使用PHP-FPM扩展
安装php-ffmpeg PHP扩展
# 使用PECL安装(不推荐,维护较少) pecl install ffmpeg # 或者使用composer安装php-ffmpeg/php-ffmpeg(推荐) composer require php-ffmpeg/php-ffmpeg
使用php-ffmpeg库示例
<?php
require_once 'vendor/autoload.php';
use FFMpeg\FFMpeg;
use FFMpeg\Coordinate\TimeCode;
use FFMpeg\Format\Video\X264;
class FFmpegProcessor {
private $ffmpeg;
public function __construct() {
$this->ffmpeg = FFMpeg::create([
'ffmpeg.binaries' => '/usr/local/bin/ffmpeg',
'ffprobe.binaries' => '/usr/local/bin/ffprobe',
'timeout' => 3600,
'ffmpeg.threads' => 12,
]);
}
/**
* 视频转码
*/
public function convertVideo($input, $output) {
$video = $this->ffmpeg->open($input);
$format = new X264('aac', 'libx264');
$format->setKiloBitrate(1000);
$video->save($format, $output);
}
/**
* 截取缩略图
*/
public function extractThumbnail($input, $output, $timeInSeconds = 1) {
$video = $this->ffmpeg->open($input);
$frame = $video->frame(TimeCode::fromSeconds($timeInSeconds));
$frame->save($output);
}
/**
* 获取视频元数据
*/
public function getMetadata($input) {
$video = $this->ffmpeg->open($input);
return [
'duration' => $video->getFormat()->get('duration'),
'width' => $video->getStreams()->videos()->first()->get('width'),
'height' => $video->getStreams()->videos()->first()->get('height'),
'codec' => $video->getStreams()->videos()->first()->get('codec_name'),
];
}
}
常见场景与命令示例
视频压缩
// 压缩到指定大小 $cmd = "-i input.mp4 -b:v 1M -maxrate 1M -bufsize 2M output.mp4"; // 压缩到指定分辨率 $cmd = "-i input.mp4 -vf scale=1280:720 output.mp4";
添加水印
$cmd = "-i input.mp4 -i watermark.png -filter_complex overlay=10:10 output.mp4";
视频剪辑
// 从5秒开始,截取10秒 $cmd = "-ss 00:00:05 -t 10 -i input.mp4 output.mp4";
音频提取
$cmd = "-i input.mp4 -vn -acodec copy output.mp3";
GIF生成
$cmd = "-i input.mp4 -vf fps=10,scale=320:-1 output.gif";
性能优化与安全建议
设置执行超时
// 设置较长的超时时间
set_time_limit(300); // 5分钟
// 或者在exec中设置
exec("timeout 300 ffmpeg -i input.mp4 output.mp4");
输入验证
// 严格验证文件路径
$allowedExtensions = ['mp4', 'avi', 'mov'];
$extension = pathinfo($inputPath, PATHINFO_EXTENSION);
if (!in_array(strtolower($extension), $allowedExtensions)) {
throw new Exception("不支持的文件格式");
}
// 使用escapeshellarg防止命令注入
$safeInput = escapeshellarg($inputPath);
$safeOutput = escapeshellarg($outputPath);
异步处理
// 对于大型文件,建议使用消息队列异步处理
// RabbitMQ, Redis队列等
$jobData = [
'input' => $inputPath,
'output' => $outputPath,
'type' => 'transcode'
];
// 将任务推送到队列
$queue->push($jobData);
错误处理
// 捕获stderr输出
$cmd = "ffmpeg -i input.mp4 output.mp4 2>&1";
exec($cmd, $output, $returnVar);
if ($returnVar !== 0) {
// 记录详细错误日志
error_log("FFmpeg错误: " . implode("\n", $output));
throw new Exception("视频处理失败");
}
完整示例:视频上传处理
<?php
class VideoUploadHandler {
private $ffmpeg;
private $allowedTypes = ['video/mp4', 'video/quicktime'];
public function __construct() {
$this->ffmpeg = new FFmpegCommand();
}
public function processUpload($file) {
// 1. 验证文件类型
if (!in_array($file['type'], $this->allowedTypes)) {
throw new Exception("不支持的文件类型");
}
// 2. 保存原始文件
$originalPath = '/uploads/' . uniqid() . '_original.mp4';
move_uploaded_file($file['tmp_name'], $originalPath);
// 3. 生成缩略图
$thumbnailPath = '/uploads/' . uniqid() . '_thumb.jpg';
$this->ffmpeg->thumbnail($originalPath, $thumbnailPath);
// 4. 压缩处理
$compressedPath = '/uploads/' . uniqid() . '_compressed.mp4';
$this->ffmpeg->transcode($originalPath, $compressedPath, [
'-b:v' => '1M',
'-maxrate' => '1M',
'-bufsize' => '2M'
]);
// 5. 获取视频信息
$info = $this->ffmpeg->getVideoInfo($compressedPath);
return [
'original' => $originalPath,
'compressed' => $compressedPath,
'thumbnail' => $thumbnailPath,
'duration' => $info['format']['duration'],
'size' => filesize($compressedPath)
];
}
}
- 推荐使用shell_exec方式:简单、灵活、易于调试
- 大型项目考虑使用php-ffmpeg库:提供了更友好的API
- 生产环境注意:超时控制、输入验证、异步处理
- 性能优化:使用GPU加速(如NVIDIA CUDA)可显著提升处理速度
选择哪种方式取决于你的项目需求、性能要求和团队熟悉程度。