本文目录导读:

我来详细介绍在PHP项目中实现视频转码的几种方法:
使用FFmpeg(推荐方案)
安装FFmpeg
# Ubuntu/Debian sudo apt-get install ffmpeg # CentOS/RHEL sudo yum install ffmpeg # macOS brew install ffmpeg
PHP代码实现
<?php
class VideoTranscoder {
private $ffmpegPath;
public function __construct($ffmpegPath = '/usr/bin/ffmpeg') {
$this->ffmpegPath = $ffmpegPath;
}
/**
* 转码视频
*/
public function transcode($inputFile, $outputFile, $format = 'mp4') {
$command = sprintf(
'%s -i %s -c:v libx264 -preset medium -crf 23 -c:a aac -b:a 128k %s 2>&1',
$this->ffmpegPath,
escapeshellarg($inputFile),
escapeshellarg($outputFile)
);
exec($command, $output, $returnCode);
return [
'success' => $returnCode === 0,
'output' => $output
];
}
/**
* 调整视频尺寸
*/
public function resize($inputFile, $outputFile, $width, $height) {
$command = sprintf(
'%s -i %s -vf scale=%d:%d -c:v libx264 -preset medium %s 2>&1',
$this->ffmpegPath,
escapeshellarg($inputFile),
$width,
$height,
escapeshellarg($outputFile)
);
exec($command, $output, $returnCode);
return $returnCode === 0;
}
/**
* 压缩视频
*/
public function compress($inputFile, $outputFile, $bitrate = '1M') {
$command = sprintf(
'%s -i %s -b:v %s -b:a 128k %s 2>&1',
$this->ffmpegPath,
escapeshellarg($inputFile),
$bitrate,
escapeshellarg($outputFile)
);
exec($command, $output, $returnCode);
return $returnCode === 0;
}
}
使用PHP-FFMpeg库
安装
composer require php-ffmpeg/php-ffmpeg
使用示例
<?php
require 'vendor/autoload.php';
use FFMpeg\FFMpeg;
use FFMpeg\Format\Video\X264;
class VideoConverter {
private $ffmpeg;
public function __construct() {
$this->ffmpeg = FFMpeg::create([
'ffmpeg.binaries' => '/usr/bin/ffmpeg',
'ffprobe.binaries' => '/usr/bin/ffprobe',
'timeout' => 3600,
'ffmpeg.threads' => 4,
]);
}
public function convert($inputPath, $outputPath) {
try {
$video = $this->ffmpeg->open($inputPath);
$format = new X264('aac', 'libx264');
$format->setKiloBitrate(1000)
->setAudioKiloBitrate(128);
$video->save($format, $outputPath);
return ['success' => true];
} catch (Exception $e) {
return ['success' => false, 'error' => $e->getMessage()];
}
}
}
异步处理方案(适合大文件)
<?php
class AsyncVideoTranscoder {
private $queueFile = '/tmp/video_queue.json';
public function addToQueue($inputFile, $outputFile, $settings = []) {
$queue = $this->getQueue();
$job = [
'id' => uniqid(),
'input' => $inputFile,
'output' => $outputFile,
'settings' => $settings,
'status' => 'pending',
'created_at' => date('Y-m-d H:i:s')
];
$queue[] = $job;
file_put_contents($this->queueFile, json_encode($queue));
return $job['id'];
}
private function getQueue() {
if (!file_exists($this->queueFile)) {
return [];
}
return json_decode(file_get_contents($this->queueFile), true) ?? [];
}
public function processQueue() {
$queue = $this->getQueue();
foreach ($queue as &$job) {
if ($job['status'] === 'pending') {
$job['status'] = 'processing';
file_put_contents($this->queueFile, json_encode($queue));
// 执行转码
$result = $this->executeTranscode($job);
$job['status'] = $result ? 'completed' : 'failed';
$job['completed_at'] = date('Y-m-d H:i:s');
file_put_contents($this->queueFile, json_encode($queue));
// 处理下一项
break;
}
}
}
private function executeTranscode($job) {
$command = sprintf(
'ffmpeg -i %s -c:v libx264 -preset medium %s -y > /dev/null 2>&1 &',
escapeshellarg($job['input']),
escapeshellarg($job['output'])
);
exec($command, $output, $returnCode);
return $returnCode === 0;
}
}
使用消息队列(高性能方案)
<?php
// 使用 Redis 作为队列
class RedisVideoTranscoder {
private $redis;
private $queueKey = 'video:transcode:queue';
public function __construct() {
$this->redis = new Redis();
$this->redis->connect('127.0.0.1', 6379);
}
public function pushJob($inputFile, $outputFile, $options = []) {
$job = [
'input' => $inputFile,
'output' => $outputFile,
'options' => $options,
'created_at' => time()
];
$this->redis->lPush($this->queueKey, json_encode($job));
return true;
}
public function processJobs() {
while (true) {
$job = $this->redis->rPop($this->queueKey);
if ($job) {
$jobData = json_decode($job, true);
$this->transcodeVideo($jobData);
}
// 短暂暂停避免CPU过载
usleep(100000);
}
}
private function transcodeVideo($job) {
$command = sprintf(
'ffmpeg -i %s -c:v libx264 -preset fast %s -y',
escapeshellarg($job['input']),
escapeshellarg($job['output'])
);
exec($command, $output, $returnCode);
// 记录转码结果
$this->logResult($job, $returnCode);
}
}
Web界面管理
<?php
// 上传和转码控制器
class VideoController {
public function upload() {
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['video'])) {
$file = $_FILES['video'];
// 验证文件
$allowedTypes = ['video/mp4', 'video/avi', 'video/mov'];
if (!in_array($file['type'], $allowedTypes)) {
return ['error' => '不支持的文件格式'];
}
$uploadPath = '/uploads/' . uniqid() . '_' . $file['name'];
move_uploaded_file($file['tmp_name'], $uploadPath);
// 开始转码
$transcoder = new VideoTranscoder();
$outputPath = '/converted/' . uniqid() . '.mp4';
$result = $transcoder->transcode($uploadPath, $outputPath);
if ($result['success']) {
return [
'success' => true,
'original' => $uploadPath,
'converted' => $outputPath,
'url' => '/download/' . basename($outputPath)
];
}
}
}
}
配置建议
<?php
// config.php
return [
'ffmpeg' => [
'path' => '/usr/bin/ffmpeg',
'timeout' => 3600, // 1小时超时
'threads' => 4, // CPU线程数
// 预设配置
'presets' => [
'high_quality' => [
'video' => 'libx264',
'bitrate' => '2000k',
'audio' => 'aac',
'audio_bitrate' => '192k'
],
'web_optimized' => [
'video' => 'libx264',
'bitrate' => '800k',
'audio' => 'aac',
'audio_bitrate' => '128k'
]
]
]
];
错误处理和日志
<?php
class VideoTranscoderErrorHandler {
private $logFile = '/var/log/video_transcoder.log';
public function logError($message, $context = []) {
$log = sprintf(
"[%s] ERROR: %s | Context: %s\n",
date('Y-m-d H:i:s'),
$message,
json_encode($context)
);
file_put_contents($this->logFile, $log, FILE_APPEND);
}
public function getStatus($jobId) {
$statusFile = "/tmp/transcoder_{$jobId}.status";
if (file_exists($statusFile)) {
return json_decode(file_get_contents($statusFile), true);
}
return ['status' => 'unknown'];
}
}
注意事项
- 性能优化:大文件转码建议使用异步队列
- 资源限制:设置适当的超时和内存限制
- 安全考虑:严格验证上传文件,防止命令注入
- 监控报警:记录转码失败并发送通知
- 进度反馈:对长时间转码提供进度反馈
选择哪种方案取决于你的项目需求:简单转码用FFmpeg命令,复杂需求用PHP-FFMpeg库,高并发用消息队列方案。