C# 調用Ffmpeg錄制視頻流實踐


調用Ffmpeg命令行工具錄制視頻流(以flv >> mp4為例):

  .\ffmpeg.exe -i "https://···:···/remote.flv" .\···\local.mp4

針對兩種需求:

  1. 用戶指定錄制時間
  2. 用戶手動終止錄制

針對需求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設為 trueWindowStyle設為 ProcessWindowStyle.Minimized
這樣可以通過:

   Process.CloseMainWindow()

來結束進程,這種方式的問題是會彈出一個窗口。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM