本文目录导读:

在 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');
?>
注意事项
- 安全性:始终使用
escapeshellarg()处理用户输入 - 性能:对于大视频文件,考虑异步处理
- 错误处理:始终检查返回码和输出
- 内存管理:处理大量帧时注意内存使用
- 格式支持:确保 FFmpeg 已安装所需编解码器
这些方法中,使用 FFmpeg 命令行是最稳定和功能最全面的方案,建议优先使用。