System.Diagnostics.Process 啟動外部程序Web.config 的 <appSettings><appSettings>
<!-- ffmpeg 執行檔完整路徑 -->
<add key="ffmpegPath" value="E:\ffmpeg\ffmpeg.exe" />
<!-- 縮圖解析度 寬x高 -->
<add key="thumbnailSize" value="240x180" />
</appSettings>
using System;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Web;
public class VideoThumbnailer {
private readonly string _ffmpegPath;
private readonly string _size;
public VideoThumbnailer() {
_ffmpegPath = ConfigurationManager.AppSettings["ffmpegPath"];
_size = ConfigurationManager.AppSettings["thumbnailSize"]; // 如 "240x180"
}
///
/// 從影片擷取第 1 幀縮圖
///
/// 虛擬路徑,如 ~/Videos/abc.mp4
/// 縮圖虛擬路徑;失敗回傳空字串
public string CaptureThumbnail(string videoVirtualPath) {
if (string.IsNullOrEmpty(_ffmpegPath) || !File.Exists(_ffmpegPath)) {
LogError("ffmpeg.exe 不存在或未設定");
return "";
}
string videoPhysicalPath = HttpContext.Current.Server.MapPath(videoVirtualPath);
if (!File.Exists(videoPhysicalPath)) {
LogError($"影片檔案不存在: {videoPhysicalPath}");
return "";
}
// 輸出圖片路徑:同目錄,同檔名,副檔名改 .jpg
string imagePhysicalPath = Path.ChangeExtension(videoPhysicalPath, ".jpg");
string imageVirtualPath = Path.ChangeExtension(videoVirtualPath, ".jpg");
// 組裝 ffmpeg 參數
// -i 輸入檔 -y 覆蓋 -f image2 輸出格式 -t 0.001 只取 1ms -s 解析度
string args = $"-i \"{videoPhysicalPath}\" -y -f image2 -t 0.001 -s {_size} \"{imagePhysicalPath}\"";
var startInfo = new ProcessStartInfo {
FileName = _ffmpegPath,
Arguments = args,
WindowStyle = ProcessWindowStyle.Hidden,
CreateNoWindow = true,
UseShellExecute = false,
RedirectStandardError = true, // 捕捉錯誤輸出
RedirectStandardOutput = true
};
try {
using (Process proc = Process.Start(startInfo)) {
// 等待完成,最長 30 秒
if (!proc.WaitForExit(30000)) {
proc.Kill();
LogError("ffmpeg 執行逾時");
return "";
}
string stderr = proc.StandardError.ReadToEnd();
if (proc.ExitCode != 0) {
LogError($"ffmpeg 失敗 (ExitCode={proc.ExitCode}): {stderr}");
return "";
}
}
} catch (Exception ex) {
LogError($"啟動 ffmpeg 例外: {ex.Message}");
return "";
}
// 檔案系統寫入可能延遲,輪詢確認
for (int i = 0; i < 10; i++) { // 最多等 5 秒
if (File.Exists(imagePhysicalPath) && new FileInfo(imagePhysicalPath).Length > 0) {
return imageVirtualPath;
}
System.Threading.Thread.Sleep(500);
}
LogError("縮圖檔案未在預期時間內生成");
return "";
}
private void LogError(string msg) {
// 實務請接入 log4net / Serilog / ILogger
System.Diagnostics.Debug.WriteLine($"[VideoThumbnailer] {msg}");
}
}
protected void btnUpload_Click(object sender, EventArgs e) {
if (fuVideo.HasFile) {
string savePath = "~/Videos/" + Guid.NewGuid() + Path.GetExtension(fuVideo.FileName);
string physical = Server.MapPath(savePath);
fuVideo.SaveAs(physical);
var thumbner = new VideoThumbnailer();
string thumbUrl = thumbner.CaptureThumbnail(savePath);
if (!string.IsNullOrEmpty(thumbUrl)) {
imgThumb.ImageUrl = thumbUrl;
lblMsg.Text = "上傳並擷取縮圖成功!";
} else {
lblMsg.Text = "影片上傳成功,但縮圖擷取失敗。";
}
}
}
| 現象 | 原因與對策 |
|---|---|
| 縮圖生成失敗 | 1. ffmpeg 版本不支援該編碼 (如 H.265 需較新版) 2. 影片加密/損壞 3. 路徑含中文/空格未加雙引號 |
| 權限錯誤 | IIS_IUSRS 需有影片資料夾寫入權限、ffmpeg.exe 讀取/執行權限 |
| 執行緒阻塞 | 改用非同步 Task.Run 或掛載訊息佇列 (Hangfire/Quartz) 背景處理 |
| 無法取得錯誤訊息 | 已在範例中加入 RedirectStandardError 讀取 stderr |
| FLV 線上錄製影片 | 通常相容性最好,幾乎不會失敗 |
若不想部署外部 exe,可考慮: