調用Ffmpeg命令行工具錄制視頻流(以flv >> mp4為例):
.\ffmpeg.exe -i "https://···:···/remote.flv" .\···\local.mp4
針對兩種需求:
- 用戶指定錄制時間
- 用戶手動終止錄制
針對需求1,可以通過參數實現
https://ffmpeg.org/ffmpeg.html#Main-options
-t duration
在 -i 之前使用,可以限制從數據源讀取數據的持續時間
例如,希望錄制30s的視頻可以:
.\ffmpeg.exe -i "https://···:···/remote.flv" -t 00:00:30 .\···\local.mp4
其他的time duaring格式可以參照https://ffmpeg.org/ffmpeg-utils.html#date-syntax
而對於需求2,則需要手動結束轉碼程序,以在windows terminal中運行轉碼命令為例,經過測試:
- 直接關閉窗口
- 輸入:q
- 輸入:Ctrl+C
錄制的視頻可以正常播放。 - 直接結束進程
視頻無法打開。
創建ffmpeg進程,將pid存入字典
// uid >> ffmpeg process id
private static ConcurrentDictionary<string, int> ffmpegs = new ConcurrentDictionary<string,int>();
[HttpPost]
public string StartRecordJob(args ...)
{
const string ffmpegPath = "c:/···/ffmpeg.exe";
var output = @"c:/···/local.mp4";
var cmdStr = $@"-i ""{url}"" {output} -y";
var startInfo = new ProcessStartInfo
{
FileName = ffmpegPath,
Arguments = cmdStr,
// 不調用Shell執行
UseShellExecute = false,
CreateNoWindow = true,
};
var rp = Process.Start(startInfo);
var uid = Regex.Replace(Convert.ToBase64String(Guid.NewGuid().ToByteArray()), "[/+=]", "");
var ok = rp != null && ffmpegs.TryAdd(uid, rp.Id);
return $"{uid}";
}
在另一個請求中,根據ffmpeg任務的uid,獲取進程id,對其進行操作
我的做法是,向進程發送退出(Ctrl+C)命令,以下做法依賴windows環境
https://blog.codetitans.pl/post/sending-ctrl-c-signal-to-another-application-on-windows/
[DllImport("kernel32.dll", SetLastError = true)]
static extern bool AttachConsole(uint dwProcessId);
[DllImport("kernel32.dll", SetLastError = true, ExactSpelling = true)]
static extern bool FreeConsole();
[DllImport("kernel32.dll")]
static extern bool SetConsoleCtrlHandler(ConsoleCtrlDelegate handler, bool add);
// Delegate type to be used as the Handler Routine for SCCH
delegate Boolean ConsoleCtrlDelegate(CtrlTypes type);
// Enumerated type for the control messages sent to the handler routine
enum CtrlTypes : uint
{
CTRL_C_EVENT = 0,
CTRL_BREAK_EVENT,
CTRL_CLOSE_EVENT,
CTRL_LOGOFF_EVENT = 5,
CTRL_SHUTDOWN_EVENT
}
[DllImport("kernel32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
private static extern bool GenerateConsoleCtrlEvent(CtrlTypes dwCtrlEvent, uint dwProcessGroupId);
public static void StopProgram(uint pid)
{
// It's impossible to be attached to 2 consoles at the same time,
// so release the current one.
FreeConsole();
// This does not require the console window to be visible.
if (AttachConsole(pid))
{
// Disable Ctrl-C handling for our program
SetConsoleCtrlHandler(null, true);
GenerateConsoleCtrlEvent(CtrlTypes.CTRL_C_EVENT, 0);
// Must wait here. If we don't and re-enable Ctrl-C
// handling below too fast, we might terminate ourselves.
Thread.Sleep(2000);
FreeConsole();
// Re-enable Ctrl-C handling or any subsequently started
// programs will inherit the disabled state.
SetConsoleCtrlHandler(null, false);
}
}
[HttpDelete]
public string StopRecordJob(string uid)
{
try
{
threads.TryGetValue(uid, out var p);
StopProgram((uint)p);
return "true";
}
catch (Exception e)
{
···
return "not found:"+ p;
}
}
還有一種做法是將startInfo中的 UseShellExecute
設為 true
,WindowStyle
設為 ProcessWindowStyle.Minimized
。
這樣可以通過:
Process.CloseMainWindow()
來結束進程,這種方式的問題是會彈出一個窗口。