如何用PHP项目实现视频转码?

wen java案例 1

本文目录导读:

如何用PHP项目实现视频转码?

  1. 使用FFmpeg(推荐方案)
  2. 使用PHP-FFMpeg库
  3. 异步处理方案(适合大文件)
  4. 使用消息队列(高性能方案)
  5. Web界面管理
  6. 配置建议
  7. 错误处理和日志
  8. 注意事项

我来详细介绍在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'];
    }
}

注意事项

  1. 性能优化:大文件转码建议使用异步队列
  2. 资源限制:设置适当的超时和内存限制
  3. 安全考虑:严格验证上传文件,防止命令注入
  4. 监控报警:记录转码失败并发送通知
  5. 进度反馈:对长时间转码提供进度反馈

选择哪种方案取决于你的项目需求:简单转码用FFmpeg命令,复杂需求用PHP-FFMpeg库,高并发用消息队列方案。

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