2020-11-23 16:51:05 +01:00
|
|
|
|
using Flowframes.IO;
|
|
|
|
|
|
using Flowframes.OS;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Diagnostics;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Text;
|
2021-01-30 13:09:59 +01:00
|
|
|
|
using System.Text.RegularExpressions;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
using System.Threading.Tasks;
|
2021-01-30 13:09:59 +01:00
|
|
|
|
using Flowframes.MiscUtils;
|
2021-04-18 18:11:47 +02:00
|
|
|
|
using Microsoft.VisualBasic;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
|
|
|
|
|
|
namespace Flowframes
|
|
|
|
|
|
{
|
|
|
|
|
|
class AvProcess
|
|
|
|
|
|
{
|
2021-03-10 20:45:48 +01:00
|
|
|
|
public static Process lastAvProcess;
|
2021-02-01 21:50:05 +01:00
|
|
|
|
public static Stopwatch timeSinceLastOutput = new Stopwatch();
|
2021-02-16 11:27:00 +01:00
|
|
|
|
public enum TaskType { ExtractFrames, ExtractOther, Encode, GetInfo, Merge, Other };
|
2021-01-06 22:51:04 +01:00
|
|
|
|
public static TaskType lastTask = TaskType.Other;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
|
|
|
|
|
|
public static string lastOutputFfmpeg;
|
|
|
|
|
|
|
|
|
|
|
|
public enum LogMode { Visible, OnlyLastLine, Hidden }
|
|
|
|
|
|
static LogMode currentLogMode;
|
2021-02-01 14:50:08 +01:00
|
|
|
|
static bool showProgressBar;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
|
2021-03-02 15:06:44 +01:00
|
|
|
|
static string defLogLevel = "warning";
|
|
|
|
|
|
|
2021-03-10 20:45:48 +01:00
|
|
|
|
public static void Kill()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (lastAvProcess == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
OSUtils.KillProcessTree(lastAvProcess.Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception e)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Log($"Failed to kill lastAvProcess process tree: {e.Message}", true);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-01 14:50:08 +01:00
|
|
|
|
public static async Task RunFfmpeg(string args, LogMode logMode, TaskType taskType = TaskType.Other, bool progressBar = false)
|
2020-11-30 20:32:33 +01:00
|
|
|
|
{
|
2021-03-02 15:06:44 +01:00
|
|
|
|
await RunFfmpeg(args, "", logMode, defLogLevel, taskType, progressBar);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task RunFfmpeg(string args, LogMode logMode, string loglevel, TaskType taskType = TaskType.Other, bool progressBar = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
await RunFfmpeg(args, "", logMode, loglevel, taskType, progressBar);
|
2020-11-30 20:32:33 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-01 14:50:08 +01:00
|
|
|
|
public static async Task RunFfmpeg(string args, string workingDir, LogMode logMode, TaskType taskType = TaskType.Other, bool progressBar = false)
|
2021-03-02 15:06:44 +01:00
|
|
|
|
{
|
|
|
|
|
|
await RunFfmpeg(args, workingDir, logMode, defLogLevel, taskType, progressBar);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task RunFfmpeg(string args, string workingDir, LogMode logMode, string loglevel, TaskType taskType = TaskType.Other, bool progressBar = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
lastOutputFfmpeg = "";
|
|
|
|
|
|
currentLogMode = logMode;
|
2021-02-01 14:50:08 +01:00
|
|
|
|
showProgressBar = progressBar;
|
2021-01-04 14:27:34 +01:00
|
|
|
|
Process ffmpeg = OSUtils.NewProcess(true);
|
2021-02-01 21:50:05 +01:00
|
|
|
|
timeSinceLastOutput.Restart();
|
2021-03-10 20:45:48 +01:00
|
|
|
|
lastAvProcess = ffmpeg;
|
2021-01-06 22:51:04 +01:00
|
|
|
|
lastTask = taskType;
|
2021-03-02 15:06:44 +01:00
|
|
|
|
|
|
|
|
|
|
if (string.IsNullOrWhiteSpace(loglevel))
|
|
|
|
|
|
loglevel = defLogLevel;
|
|
|
|
|
|
|
2021-04-27 16:51:46 +02:00
|
|
|
|
string beforeArgs = $"-hide_banner -stats -loglevel {loglevel} -y";
|
2021-03-22 21:34:10 +01:00
|
|
|
|
|
2020-11-30 20:32:33 +01:00
|
|
|
|
if(!string.IsNullOrWhiteSpace(workingDir))
|
2021-03-22 21:34:10 +01:00
|
|
|
|
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {workingDir.Wrap()} & {Path.Combine(GetAvDir(), "ffmpeg.exe").Wrap()} {beforeArgs} {args}";
|
2020-11-30 20:32:33 +01:00
|
|
|
|
else
|
2021-03-22 21:34:10 +01:00
|
|
|
|
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg.exe {beforeArgs} {args}";
|
2021-03-02 15:06:44 +01:00
|
|
|
|
|
2021-04-27 16:51:46 +02:00
|
|
|
|
if (logMode != LogMode.Hidden) Logger.Log("Running FFmpeg...", false);
|
2021-03-22 21:34:10 +01:00
|
|
|
|
Logger.Log($"ffmpeg {beforeArgs} {args}", true, false, "ffmpeg");
|
2021-04-04 20:10:59 +02:00
|
|
|
|
ffmpeg.OutputDataReceived += FfmpegOutputHandler;
|
|
|
|
|
|
ffmpeg.ErrorDataReceived += FfmpegOutputHandler;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
ffmpeg.Start();
|
|
|
|
|
|
ffmpeg.BeginOutputReadLine();
|
|
|
|
|
|
ffmpeg.BeginErrorReadLine();
|
2021-02-02 16:07:36 +01:00
|
|
|
|
|
2020-11-23 16:51:05 +01:00
|
|
|
|
while (!ffmpeg.HasExited)
|
2020-12-27 22:52:14 +01:00
|
|
|
|
await Task.Delay(1);
|
2021-02-02 16:07:36 +01:00
|
|
|
|
|
|
|
|
|
|
if(progressBar)
|
|
|
|
|
|
Program.mainForm.SetProgress(0);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-25 12:40:17 +01:00
|
|
|
|
static void FfmpegOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2021-02-01 21:50:05 +01:00
|
|
|
|
timeSinceLastOutput.Restart();
|
2021-04-04 20:10:59 +02:00
|
|
|
|
|
2021-05-03 17:34:18 +02:00
|
|
|
|
if (Interpolate.canceled || outLine == null || outLine.Data == null)
|
2020-11-26 20:17:18 +01:00
|
|
|
|
return;
|
2021-04-04 20:10:59 +02:00
|
|
|
|
|
2020-11-26 20:17:18 +01:00
|
|
|
|
string line = outLine.Data;
|
2021-01-06 21:44:09 +01:00
|
|
|
|
lastOutputFfmpeg = lastOutputFfmpeg + "\n" + line;
|
2021-04-04 20:10:59 +02:00
|
|
|
|
|
2020-11-23 16:51:05 +01:00
|
|
|
|
bool hidden = currentLogMode == LogMode.Hidden;
|
2021-04-18 18:11:47 +02:00
|
|
|
|
|
2021-04-26 18:26:16 +02:00
|
|
|
|
if (HideMessage(line)) // Don't print certain warnings
|
2021-04-18 18:11:47 +02:00
|
|
|
|
hidden = true;
|
|
|
|
|
|
|
2020-11-23 16:51:05 +01:00
|
|
|
|
bool replaceLastLine = currentLogMode == LogMode.OnlyLastLine;
|
2021-04-27 16:51:46 +02:00
|
|
|
|
|
|
|
|
|
|
if (line.StartsWith("frame=")) // Format stats
|
|
|
|
|
|
{
|
|
|
|
|
|
line = line.Remove("q=-0.0").Remove("size=N/A").Remove("bitrate=N/A").Replace("frame=", "Frame: ")
|
|
|
|
|
|
.Replace("fps=", "FPS: ").Replace("q=", "QP: ").Replace("time=", "Time: ").Replace("speed=", "Relative Speed: ")
|
|
|
|
|
|
.Replace("bitrate=", "Bitrate: ").Replace("Lsize=", "Size: ").Replace("size=", "Size: ").TrimWhitespaces();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Logger.Log(line, hidden, replaceLastLine, "ffmpeg");
|
2020-11-26 20:17:18 +01:00
|
|
|
|
|
2021-02-14 22:23:16 +01:00
|
|
|
|
if (line.Contains(".srt: Invalid data found"))
|
2021-02-25 19:21:11 +01:00
|
|
|
|
Logger.Log($"Warning: Failed to encode subtitle track {line.Split(':')[2]}. This track will be missing in the output file.");
|
2021-02-14 22:23:16 +01:00
|
|
|
|
|
|
|
|
|
|
if (line.Contains("Could not open file"))
|
2021-01-06 21:44:09 +01:00
|
|
|
|
Interpolate.Cancel($"FFmpeg Error: {line}");
|
2021-01-14 17:55:00 +01:00
|
|
|
|
|
2021-04-27 16:51:46 +02:00
|
|
|
|
if (line.Contains("No NVENC capable devices found") || line.MatchesWildcard("*nvcuda.dll*"))
|
2021-01-14 17:55:00 +01:00
|
|
|
|
Interpolate.Cancel($"FFmpeg Error: {line}\nMake sure you have an NVENC-capable Nvidia GPU.");
|
2021-01-30 13:09:59 +01:00
|
|
|
|
|
2021-04-30 16:54:54 +02:00
|
|
|
|
if (!hidden && showProgressBar && line.Contains("Time:"))
|
2021-01-30 13:09:59 +01:00
|
|
|
|
{
|
2021-04-30 16:54:54 +02:00
|
|
|
|
Regex timeRegex = new Regex("(?<=Time:).*(?= )");
|
2021-01-30 13:09:59 +01:00
|
|
|
|
UpdateFfmpegProgress(timeRegex.Match(line).Value);
|
|
|
|
|
|
}
|
2020-11-23 16:51:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-04-26 18:26:16 +02:00
|
|
|
|
static bool HideMessage (string msg)
|
|
|
|
|
|
{
|
2021-04-27 16:51:46 +02:00
|
|
|
|
string[] hiddenMsgs = new string[] { "can produce invalid output", "pixel format" };
|
2021-04-26 18:26:16 +02:00
|
|
|
|
|
|
|
|
|
|
foreach (string str in hiddenMsgs)
|
|
|
|
|
|
if (msg.MatchesWildcard($"*{str}*"))
|
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
static void FfmpegOutputHandlerSilent (object sendingProcess, DataReceivedEventArgs outLine)
|
|
|
|
|
|
{
|
2021-02-04 20:11:55 +01:00
|
|
|
|
timeSinceLastOutput.Restart();
|
2021-05-03 17:34:18 +02:00
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
if (outLine == null || outLine.Data == null || outLine.Data.Trim().Length < 2)
|
|
|
|
|
|
return;
|
2021-05-03 17:34:18 +02:00
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
string line = outLine.Data;
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(lastOutputFfmpeg))
|
|
|
|
|
|
lastOutputFfmpeg += "\n";
|
2021-05-03 17:34:18 +02:00
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
lastOutputFfmpeg = lastOutputFfmpeg + line;
|
|
|
|
|
|
Logger.Log(line, true, false, "ffmpeg");
|
2021-02-07 18:12:41 +01:00
|
|
|
|
|
|
|
|
|
|
if (showProgressBar && line.Contains("time="))
|
|
|
|
|
|
{
|
|
|
|
|
|
Regex timeRegex = new Regex("(?<=time=).*(?= )");
|
|
|
|
|
|
UpdateFfmpegProgress(timeRegex.Match(line).Value);
|
|
|
|
|
|
}
|
2020-12-27 22:52:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-07 18:12:41 +01:00
|
|
|
|
public static async Task<string> GetFfmpegOutputAsync(string args, bool setBusy = false, bool progressBar = false)
|
2020-12-27 22:52:14 +01:00
|
|
|
|
{
|
2021-02-04 20:11:55 +01:00
|
|
|
|
timeSinceLastOutput.Restart();
|
|
|
|
|
|
if (Program.busy) setBusy = false;
|
2020-12-27 22:52:14 +01:00
|
|
|
|
lastOutputFfmpeg = "";
|
2021-02-07 18:12:41 +01:00
|
|
|
|
showProgressBar = progressBar;
|
2020-12-27 22:52:14 +01:00
|
|
|
|
Process ffmpeg = OSUtils.NewProcess(true);
|
2021-03-10 20:45:48 +01:00
|
|
|
|
lastAvProcess = ffmpeg;
|
2020-12-27 22:52:14 +01:00
|
|
|
|
ffmpeg.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffmpeg.exe -hide_banner -y -stats {args}";
|
2021-03-22 21:34:10 +01:00
|
|
|
|
Logger.Log($"ffmpeg {args}", true, false, "ffmpeg");
|
2021-02-04 20:11:55 +01:00
|
|
|
|
if (setBusy) Program.mainForm.SetWorking(true);
|
2021-05-06 12:08:03 +02:00
|
|
|
|
lastOutputFfmpeg = await OSUtils.GetOutputAsync(ffmpeg);
|
2021-02-04 20:11:55 +01:00
|
|
|
|
while (!ffmpeg.HasExited) await Task.Delay(50);
|
|
|
|
|
|
while(timeSinceLastOutput.ElapsedMilliseconds < 200) await Task.Delay(50);
|
|
|
|
|
|
if (setBusy) Program.mainForm.SetWorking(false);
|
2020-12-27 22:52:14 +01:00
|
|
|
|
return lastOutputFfmpeg;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-23 16:51:05 +01:00
|
|
|
|
public static string GetFfprobeOutput (string args)
|
|
|
|
|
|
{
|
|
|
|
|
|
Process ffprobe = OSUtils.NewProcess(true);
|
2020-12-27 22:52:14 +01:00
|
|
|
|
ffprobe.StartInfo.Arguments = $"{GetCmdArg()} cd /D {GetAvDir().Wrap()} & ffprobe.exe {args}";
|
2021-03-22 21:34:10 +01:00
|
|
|
|
Logger.Log($"ffprobe {args}", true, false, "ffmpeg");
|
2020-11-23 16:51:05 +01:00
|
|
|
|
ffprobe.Start();
|
|
|
|
|
|
ffprobe.WaitForExit();
|
|
|
|
|
|
string output = ffprobe.StandardOutput.ReadToEnd();
|
|
|
|
|
|
string err = ffprobe.StandardError.ReadToEnd();
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(err)) output += "\n" + err;
|
|
|
|
|
|
return output;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-01 14:50:08 +01:00
|
|
|
|
public static void UpdateFfmpegProgress(string ffmpegTime)
|
2021-01-30 13:09:59 +01:00
|
|
|
|
{
|
2021-02-23 12:13:30 +01:00
|
|
|
|
Form1 form = Program.mainForm;
|
2021-02-23 14:44:03 +01:00
|
|
|
|
long currInDuration = (form.currInDurationCut < form.currInDuration) ? form.currInDurationCut : form.currInDuration;
|
2021-02-23 12:13:30 +01:00
|
|
|
|
|
|
|
|
|
|
if (currInDuration < 1)
|
2021-02-01 14:50:08 +01:00
|
|
|
|
{
|
|
|
|
|
|
Program.mainForm.SetProgress(0);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-23 12:13:30 +01:00
|
|
|
|
long total = currInDuration / 100;
|
|
|
|
|
|
long current = FormatUtils.TimestampToMs(ffmpegTime);
|
2021-01-30 13:09:59 +01:00
|
|
|
|
int progress = Convert.ToInt32(current / total);
|
|
|
|
|
|
Program.mainForm.SetProgress(progress);
|
|
|
|
|
|
}
|
2020-12-10 16:10:52 +01:00
|
|
|
|
|
2020-11-23 16:51:05 +01:00
|
|
|
|
static string GetAvDir ()
|
|
|
|
|
|
{
|
2021-03-11 12:58:18 +01:00
|
|
|
|
return Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
}
|
2020-12-27 22:52:14 +01:00
|
|
|
|
|
|
|
|
|
|
static string GetCmdArg ()
|
|
|
|
|
|
{
|
|
|
|
|
|
return "/C";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task SetBusyWhileRunning ()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (Program.busy) return;
|
|
|
|
|
|
|
|
|
|
|
|
await Task.Delay(100);
|
2021-03-10 20:45:48 +01:00
|
|
|
|
while(!lastAvProcess.HasExited)
|
2020-12-27 22:52:14 +01:00
|
|
|
|
await Task.Delay(10);
|
|
|
|
|
|
}
|
2020-11-23 16:51:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|