從視頻檔案擷取指定幀作為縮圖/封面。關鍵難點:**手機拍攝視頻常帶旋轉中繼資料 (rotate metadata)**,直接擷取畫面會呈現側躺/倒轉狀態,需讀取旋轉角度並修正。
ffmpeg 擴展 (php-ffmpeg / php-ffmpeg-php) 或直接呼叫 CLIffmpeg、ffprobe (通常同套件)use FFMpeg\FFMpeg;
use FFMpeg\Coordinate\TimeCode;
$ffmpeg = FFMpeg::create();
$video = $ffmpeg->open('input.mp4');
// 1. 讀取旋轉中繼資料
$rotation = 0;
$streams = $video->getStreams();
foreach ($streams as $stream) {
if ($stream->get('codec_type') === 'video') {
$rotation = (int)($stream->get('tags')['rotate'] ?? 0);
break;
}
}
// 2. 擷取第 1 秒畫面
$frame = $video->frame(TimeCode::fromSeconds(1));
$image = $frame->toGDImage();
// 3. 修正旋轉 (GD imagerotate 角度為逆時針)
if ($rotation !== 0) {
// ffprobe 旋轉角度為順時針,GD 為逆時針
$image = imagerotate($image, 360 - $rotation, 0);
}
// 4. 輸出
imagejpeg($image, 'thumb.jpg', 90);
imagedestroy($image);
function get_video_rotation($videoPath) {
$cmd = "ffprobe -v error -select_streams v:0 -show_entries stream_tags=rotate -of default=nw=1:nk=1 " . escapeshellarg($videoPath);
$output = shell_exec($cmd);
return (int)trim($output);
}
function extract_thumb($videoPath, $outputPath, $time = '00:00:01', $rotation = 0) {
$filter = '';
switch ($rotation) {
case 90: $filter = 'transpose=1'; break; // 逆時針 90
case 180: $filter = 'transpose=2,transpose=2'; break; // 180
case 270: $filter = 'transpose=2'; break; // 逆時針 270 = 順時針 90
}
$cmd = "ffmpeg -y -ss " . escapeshellarg($time) . " -i " . escapeshellarg($videoPath);
if ($filter) {
$cmd .= " -vf " . escapeshellarg($filter);
}
$cmd .= " -vframes 1 -q:v 2 " . escapeshellarg($outputPath) . " 2>&1";
exec($cmd, $output, $returnVar);
return $returnVar === 0;
}
// 使用
$rotation = get_video_rotation('video.mp4');
extract_thumb('video.mp4', 'thumb.jpg', '00:00:01', $rotation);
function fix_video_rotation($input, $output) {
// transpose=2 為 180 旋轉,metadata:s:v:0 rotate=0 清除標記
$cmd = "ffmpeg -y -i " . escapeshellarg($input) . " -vf transpose=2 -metadata:s:v:0 rotate=0 -c:a copy " . escapeshellarg($output) . " 2>&1";
exec($cmd, $out, $ret);
return $ret === 0;
}
| rotate 標記 | 實際畫面 | transpose 參數 | 說明 |
|---|---|---|---|
| 0 | 正常 | 無 | 無需處理 |
| 90 | 順時針 90° | transpose=1 | 逆時針 90° 修正 |
| 180 | 倒轉 180° | transpose=2,transpose=2 | 兩次 90° |
| 270 | 逆時針 90° (順時針 270°) | transpose=2 | 逆時針 270° |
| 問題 | 原因 | 解決 |
|---|---|---|
| imagerotate 黑邊/背景色 | 第四參數背景色預設 0 (黑) | 用 imagecolorallocatealpha($img, 255,255,255, 127) 透明背景 |
| 大視頻擷取慢 | -ss 在 -i 後面會解碼到該時間點 | -ss 放在 -i 前面 (快速定位,非關鍵幀) 或用 -ss 00:00:01 -i input -vframes 1 |
| 關鍵幀不準確 | 快速定位落在非關鍵幀 | 接受近似幀、或用 -ss 在 -i 後 (慢但精確) |
| PHP 執行超時 | ffmpeg 轉碼耗時 | set_time_limit(0)、或異步佇列處理 |
| 權限被拒 | PHP 執行身份無 ffmpeg 執行權 | 確保 www-data 可執行 /usr/bin/ffmpeg、輸出目錄可寫 |
-hwaccel cuda -c:v h264_nvenc (需 NVIDIA GPU + NVENC)ffprobe 讀取角度 → ffmpeg -vf transpose=X 一步到位修正,效能最佳、無 GD 記憶體限制-metadata:s:v:0 rotate=0),避免播放器重複旋轉掌握此流程,即可正確處理各種手機上傳視頻的縮圖生成與播放相容性問題。