前置需求
- 伺服器已安裝
ffmpeg 並加入系統 PATH
- PHP 可執行
exec()、system() 等指令函數(未被 disable_functions 限制)
- Web 使用者對影片檔案具讀取權限,對輸出目錄具寫入權限
功能一:擷取影片縮圖
<?php
/**
* 擷取影片指定時間點的縮圖
*
* @param string $videoPath 影片檔案完整路徑
* @param string $outputPath 輸出圖片完整路徑
* @param float $timePoint 擷取時間點(秒),預設第 1 秒
* @param int $width 縮圖寬度,預設 320
* @param int $height 縮圖高度,預設 240
* @return bool 是否成功
*/
function getVideoThumbnail($videoPath, $outputPath, $timePoint = 1, $width = 320, $height = 240)
{
if (!file_exists($videoPath)) {
trigger_error("影片檔案不存在: $videoPath", E_USER_WARNING);
return false;
}
// 確保輸出目錄存在
$dir = dirname($outputPath);
if (!is_dir($dir)) {
mkdir($dir, 0755, true);
}
// 建構 ffmpeg 指令
// -ss 指定時間點(輸入前較快、輸出後較精準),此處放輸入前
// -vframes 1 僅擷取 1 幀
// -f image2 強制圖片格式
$cmd = sprintf(
'ffmpeg -y -ss %.3f -i %s -vframes 1 -s %dx%d %s 2>&1',
$timePoint,
escapeshellarg($videoPath),
$width,
$height,
escapeshellarg($outputPath)
);
$output = [];
$returnVar = 0;
exec($cmd, $output, $returnVar);
if ($returnVar !== 0) {
error_log("FFmpeg 縮圖失敗: " . implode("\n", $output));
return false;
}
return file_exists($outputPath) && filesize($outputPath) > 0;
}
// 使用範例
getVideoThumbnail('/var/www/videos/demo.mp4', '/var/www/thumbs/demo_1s.jpg', 3);
?>
功能二:取得影片總長度與建立時間
<?php
/**
* 取得影片總長度(秒)與檔案建立時間
*
* @param string $videoPath 影片檔案路徑
* @return array|false ['duration' => 秒數, 'created_at' => 'Y-m-d H:i:s'] 或 false
*/
function getVideoInfo($videoPath)
{
if (!file_exists($videoPath)) {
return false;
}
// 使用 ffprobe 更精準、輸出格式固定 (JSON)
$cmd = sprintf(
'ffprobe -v error -show_entries format=duration -of json %s 2>&1',
escapeshellarg($videoPath)
);
$output = [];
$returnVar = 0;
exec($cmd, $output, $returnVar);
if ($returnVar !== 0) {
error_log("ffprobe 失敗: " . implode("\n", $output));
return false;
}
$json = json_decode(implode('', $output), true);
$duration = isset($json['format']['duration']) ? (float)$json['format']['duration'] : 0;
$createdAt = date('Y-m-d H:i:s', filectime($videoPath));
return [
'duration' => $duration, // 總秒數 (浮點數)
'duration_formatted' => gmdate('H:i:s', $duration), // 格式化 HH:MM:SS
'created_at' => $createdAt,
];
}
// 使用範例
$info = getVideoInfo('/var/www/videos/demo.mp4');
if ($info) {
echo "長度: {$info['duration_formatted']} ({$info['duration']} 秒)\n";
echo "建立時間: {$info['created_at']}\n";
}
?>
常見問題排查
| 症狀 | 可能原因 | 解決方向 |
| exec() 回傳 127 | 找不到 ffmpeg 指令 | 確認 ffmpeg 在 PATH 中,或使用絕對路徑 /usr/bin/ffmpeg |
| 輸出檔案 0 bytes | 權限不足 / 參數錯誤 | 檢查 Web 使用者對輸出目錄寫入權限;加 2>&1 捕獲錯誤訊息 |
| 縮圖時間點不準 | -ss 放在 -i 之後 | 快速模式:-ss 放在 -i 前;精準模式:放在後但較慢 |
| 中文路徑失敗 | Shell 轉義問題 | 一律使用 escapeshellarg() 包裹路徑 |
效能建議
- 大量影片處理請使用**佇列系統**(如 Laravel Queue、Redis + Worker)非同步執行,避免阻塞 Web 程序。
- 縮圖尺寸建議固定幾組規格(如 320x180, 640x360),避免任意尺寸造成儲存浪費。
- 若僅需影片長度,
ffprobe 比 ffmpeg -i 解析快得多且輸出穩定。