PHP 视频截帧怎么做

wen PHP项目 3

本文目录导读:

PHP 视频截帧怎么做

  1. 使用 FFmpeg(推荐)
  2. 使用 PHP 扩展
  3. 使用第三方库
  4. 完整的实用示例
  5. 注意事项

在 PHP 中实现视频截帧,主要有以下几种方法:

使用 FFmpeg(推荐)

这是最常用和强大的方法:

安装 FFmpeg

# Ubuntu/Debian
sudo apt-get install ffmpeg
# CentOS/RHEL
sudo yum install ffmpeg
# macOS
brew install ffmpeg

基本截帧示例

<?php
function getVideoFrame($videoPath, $outputPath, $time = '00:00:01') {
    // 基础命令
    $cmd = sprintf(
        'ffmpeg -i %s -ss %s -vframes 1 -q:v 2 %s 2>&1',
        escapeshellarg($videoPath),
        $time,
        escapeshellarg($outputPath)
    );
    exec($cmd, $output, $returnCode);
    return $returnCode === 0;
}
// 使用示例
$videoFile = 'video.mp4';
$frameFile = 'frame.jpg';
$result = getVideoFrame($videoFile, $frameFile, '00:00:05');
if ($result) {
    echo "截帧成功!";
} else {
    echo "截帧失败!";
}
?>

高级用法

<?php
class VideoFrameExtractor {
    private $ffmpegPath = 'ffmpeg';
    /**
     * 截取指定时间的帧
     */
    public function getFrameAtTime($videoPath, $time, $outputPath, $options = []) {
        $defaultOptions = [
            'quality' => 2,        // 质量(1-31,越小越好)
            'size' => null,        // 尺寸,如 '320x240'
            'framerate' => 1,      // 帧率
            'format' => 'jpg'      // 输出格式
        ];
        $options = array_merge($defaultOptions, $options);
        $cmd = $this->ffmpegPath;
        // 添加输入文件
        $cmd .= ' -i ' . escapeshellarg($videoPath);
        // 添加时间参数
        $cmd .= ' -ss ' . $time;
        // 添加质量参数
        $cmd .= ' -vframes 1 -q:v ' . $options['quality'];
        // 添加尺寸参数
        if ($options['size']) {
            $cmd .= ' -s ' . $options['size'];
        }
        // 添加输出文件
        $cmd .= ' ' . escapeshellarg($outputPath);
        // 执行命令
        exec($cmd . ' 2>&1', $output, $returnCode);
        return [
            'success' => $returnCode === 0,
            'output' => $output,
            'path' => $outputPath
        ];
    }
    /**
     * 提取视频中的所有帧
     */
    public function extractAllFrames($videoPath, $outputDir, $format = 'jpg') {
        if (!is_dir($outputDir)) {
            mkdir($outputDir, 0777, true);
        }
        $cmd = sprintf(
            '%s -i %s -vf fps=1 %s/frame_%%d.%s 2>&1',
            $this->ffmpegPath,
            escapeshellarg($videoPath),
            escapeshellarg($outputDir),
            $format
        );
        exec($cmd, $output, $returnCode);
        return $returnCode === 0;
    }
    /**
     * 获取视频信息
     */
    public function getVideoInfo($videoPath) {
        $cmd = sprintf(
            '%s -i %s 2>&1',
            $this->ffmpegPath,
            escapeshellarg($videoPath)
        );
        exec($cmd, $output);
        $info = [
            'duration' => null,
            'width' => null,
            'height' => null
        ];
        foreach ($output as $line) {
            // 获取时长
            if (preg_match('/Duration: (\d{2}):(\d{2}):(\d{2}\.\d{2})/', $line, $matches)) {
                $info['duration'] = $matches[1] . ':' . $matches[2] . ':' . $matches[3];
            }
            // 获取分辨率
            if (preg_match('/Stream.*Video:.*(\d{3,4})x(\d{3,4})/', $line, $matches)) {
                $info['width'] = $matches[1];
                $info['height'] = $matches[2];
            }
        }
        return $info;
    }
}
// 使用示例
$extractor = new VideoFrameExtractor();
// 单帧截取
$result = $extractor->getFrameAtTime(
    'video.mp4',
    '00:00:10',
    'output/frame.jpg',
    ['size' => '640x360']
);
// 提取所有帧(每秒1帧)
$result = $extractor->extractAllFrames('video.mp4', 'frames/');
// 获取视频信息
$info = $extractor->getVideoInfo('video.mp4');
echo "视频时长:" . $info['duration'];
echo "分辨率:" . $info['width'] . 'x' . $info['height'];
?>

使用 PHP 扩展

GD 库 + FFmpeg(结合使用)

<?php
function getFrameWithGD($videoPath, $time, $outputPath) {
    // 先使用ffmpeg截帧
    $tempFile = tempnam(sys_get_temp_dir(), 'frame');
    $cmd = sprintf(
        'ffmpeg -i %s -ss %s -vframes 1 %s 2>&1',
        escapeshellarg($videoPath),
        $time,
        escapeshellarg($tempFile)
    );
    exec($cmd, $output, $returnCode);
    if ($returnCode !== 0) {
        return false;
    }
    // 使用GD进行图像处理
    $image = imagecreatefromjpeg($tempFile);
    // 调整大小
    $newWidth = 800;
    $newHeight = 450;
    $resizedImage = imagecreatetruecolor($newWidth, $newHeight);
    imagecopyresampled(
        $resizedImage, $image,
        0, 0, 0, 0,
        $newWidth, $newHeight,
        imagesx($image), imagesy($image)
    );
    // 添加水印
    $watermark = imagecreatetruecolor(100, 50);
    $color = imagecolorallocate($watermark, 255, 255, 255);
    imagestring($watermark, 5, 5, 5, 'Watermark', $color);
    imagecopy($resizedImage, $watermark, 10, 10, 0, 0, 100, 50);
    // 保存图片
    imagejpeg($resizedImage, $outputPath, 90);
    // 清理资源
    imagedestroy($image);
    imagedestroy($resizedImage);
    unlink($tempFile);
    return true;
}
?>

使用第三方库

PHP-FFI(通过 FFI 调用)

<?php
// 使用 FFI 调用 FFmpeg 库(需要 FFI 扩展)
$ffi = FFI::cdef(
    "int avformat_open_input(void **ps, const char *url, void *fmt, void *options);",
    "libavformat.so"
);
// 具体实现较为复杂,推荐使用命令行方式
?>

完整的实用示例

<?php
class VideoProcessor {
    private $ffmpeg;
    public function __construct($ffmpegPath = 'ffmpeg') {
        $this->ffmpeg = $ffmpegPath;
    }
    /**
     * 生成视频缩略图(多个时间点)
     */
    public function generateThumbnails($videoPath, $outputDir, $count = 4) {
        // 获取视频信息
        $info = $this->getVideoInfo($videoPath);
        if (!$info || !$info['duration']) {
            return false;
        }
        // 解析时长
        list($h, $m, $s) = explode(':', $info['duration']);
        $totalSeconds = $h * 3600 + $m * 60 + $s;
        // 计算截取时间点
        $thumbnails = [];
        for ($i = 0; $i < $count; $i++) {
            $timePoint = $totalSeconds * ($i + 1) / ($count + 1);
            $thumbPath = $outputDir . '/thumb_' . ($i + 1) . '.jpg';
            // 格式化时间
            $timeStr = sprintf(
                '%02d:%02d:%02d',
                floor($timePoint / 3600),
                floor(($timePoint % 3600) / 60),
                floor($timePoint % 60)
            );
            // 执行截帧
            $cmd = sprintf(
                '%s -i %s -ss %s -vframes 1 -vf "thumbnail=50,scale=320:240" %s 2>&1',
                $this->ffmpeg,
                escapeshellarg($videoPath),
                $timeStr,
                escapeshellarg($thumbPath)
            );
            exec($cmd, $output, $returnCode);
            if ($returnCode === 0) {
                $thumbnails[] = $thumbPath;
            }
        }
        return $thumbnails;
    }
    /**
     * 创建视频网格预览
     */
    public function createPreviewGrid($videoPath, $outputPath, $gridWidth = 3, $gridHeight = 3) {
        $cmd = sprintf(
            '%s -i %s -vf "fps=1,scale=320:180,tile=%dx%d" -frames:v 1 %s 2>&1',
            $this->ffmpeg,
            escapeshellarg($videoPath),
            $gridWidth,
            $gridHeight,
            escapeshellarg($outputPath)
        );
        exec($cmd, $output, $returnCode);
        return $returnCode === 0;
    }
    private function getVideoInfo($videoPath) {
        $cmd = sprintf('%s -i %s 2>&1', $this->ffmpeg, escapeshellarg($videoPath));
        exec($cmd, $output);
        $info = [];
        foreach ($output as $line) {
            if (preg_match('/Duration: (\d{2}:\d{2}:\d{2}\.\d+)/', $line, $m)) {
                $info['duration'] = $m[1];
            }
        }
        return $info;
    }
}
// 使用示例
$processor = new VideoProcessor();
$thumbnails = $processor->generateThumbnails('video.mp4', 'thumbs/', 4);
$result = $processor->createPreviewGrid('video.mp4', 'preview.jpg');
?>

注意事项

  1. 安全性:始终使用 escapeshellarg() 处理用户输入
  2. 性能:对于大视频文件,考虑异步处理
  3. 错误处理:始终检查返回码和输出
  4. 内存管理:处理大量帧时注意内存使用
  5. 格式支持:确保 FFmpeg 已安装所需编解码器

这些方法中,使用 FFmpeg 命令行是最稳定和功能最全面的方案,建议优先使用。

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