2020年5月3日日曜日

C#からFFmpegを制御する

前回、FFmpegからRtmp配信をする方法について記載しました。

コマンドプロンプトからFFmpegを使用しての動画の変換やダウンロードであれば、あまり気にする必要ないかもしれませんがWPFなどから起動する場合はProcessクラスを使用します。

1.プログラムからFFmpegを起動する

まずは基本形
public void RunFfmpeg()
{
    var proc = new Process();
    proc.StartInfo.FileName = "FFmpeg.exeのパス";
    proc.StartInfo.Arguments = "FFmpegのオプション指定";
    proc.StartInfo.CreateNoWindow = true; // ウィンドウ表示しない
    proc.StartInfo.UseShellExecute = false;
    proc.Start();
}

こんな感じ。
CreateNoWindow = true
を指定すると、ウィンドウが表示されません。

2.FFmpegを停止する

放っておいて停止するような場合は構いませんが、ライブ配信を行うような場合は、終了処理が必要となります。

public void RunFfmpeg()
{
    var proc = new Process();
    proc.StartInfo.FileName = "FFmpeg.exeのパス";
    proc.StartInfo.Arguments = "FFmpegのオプション指定";
    proc.StartInfo.RedirectStandardInput = true;
    proc.StartInfo.CreateNoWindow = true; // ウィンドウ表示しない
    proc.StartInfo.UseShellExecute = false;
    proc.Start();
}

今回はRedirectStandardInput = trueを指定しています。
これで、プロセスに対してコマンドの送付が可能となります。
FFmpegは「Q」キーで停止可能となっています。
なので、停止指示は「Q」を送付すればOK

public void StopFfmpeg()
{
    StreamWriter inputWriter = proc.StandardInput;
    inputWriter.WriteLine("q");
    rtmpProc.WaitForExit(3000);
}


3.標準入力を使用して入力データを送る

今度はWEBカメラからの映像の取り込み動画に変換します。

public void RunFfmpeg()
{
    var proc = new Process();
    proc.StartInfo.FileName = "FFmpeg.exeのパス";
    proc.StartInfo.Arguments = "-i - -f image2pipe output.mp4";
    proc.StartInfo.RedirectStandardInput = true;
    proc.StartInfo.CreateNoWindow = true; // ウィンドウ表示しない
    proc.StartInfo.UseShellExecute = false;
    proc.Start();

}

Arguments の「-i -」を指定することで、StandardInputからの入力を受け付けるようになります。
また、フォーマットでは「image2pipe 」を指定します。

次にカメラからの画像の取り込みについて。
こちらのサイトではWEBカメラを使用した画像の取り込みを行っています。
C#によるUSBカメラ操作(WPF編)

画像の取り込みには、Accord.Video.DirectShowのVideoCaptureDeviceを使用します。

public event NewFrameEventHandler NewFrameGot = delegate { };

public void StartCamera()
{
    var device = new VideoCaptureDevice("カメラ名");
    device.NewFrame += NewFrameGot;
    device.Start();
}

こんな感じで、NewFrameイベントを使用します。
// コンストラクタで指定
NewFrameGot += CamDeviceCtrlNewFrameGot;

private void CamDeviceCtrlNewFrameGot(object sender, NewFrameEventArgs eventArgs)
{
    eventArgs.Frame.Save(proc.StandardInput.BaseStream, ImageFormat.Bmp);
}