PHP项目中如何使用FFmpeg?

wen PHP项目 1

本文目录导读:

PHP项目中如何使用FFmpeg?

  1. 环境准备
  2. 方法一:直接执行shell命令(推荐)
  3. 方法二:使用PHP-FPM扩展
  4. 常见场景与命令示例
  5. 性能优化与安全建议
  6. 完整示例:视频上传处理

在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)可显著提升处理速度

选择哪种方式取决于你的项目需求、性能要求和团队熟悉程度。

上一篇如何为PHP项目实现软删除功能?

下一篇当前分类已是最新一篇

抱歉,评论功能暂时关闭!