2020-12-23 18:21:31 +01:00
|
|
|
|
using Flowframes.AudioVideo;
|
|
|
|
|
|
using Flowframes.Data;
|
2020-12-02 14:27:41 +01:00
|
|
|
|
using Flowframes.IO;
|
2020-12-23 00:07:06 +01:00
|
|
|
|
using Flowframes.Main;
|
2020-12-29 16:01:24 +01:00
|
|
|
|
using Flowframes.MiscUtils;
|
2020-12-07 12:34:12 +01:00
|
|
|
|
using System;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.IO;
|
2020-12-22 19:52:37 +01:00
|
|
|
|
using System.Linq;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
|
|
using System.Threading.Tasks;
|
2020-12-23 16:13:04 +01:00
|
|
|
|
using Utils = Flowframes.AudioVideo.FFmpegUtils;
|
2020-11-23 16:51:05 +01:00
|
|
|
|
|
|
|
|
|
|
namespace Flowframes
|
|
|
|
|
|
{
|
|
|
|
|
|
class FFmpegCommands
|
|
|
|
|
|
{
|
2020-11-25 17:48:27 +01:00
|
|
|
|
static string hdrFilter = @"-vf select=gte(n\,%frNum%),zscale=t=linear:npl=100,format=gbrpf32le,zscale=p=bt709,tonemap=tonemap=hable:desat=0,zscale=t=bt709:m=bt709:r=tv,format=yuv420p";
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
static string videoEncArgs = "-pix_fmt yuv420p -movflags +faststart";
|
2020-11-24 12:28:47 +01:00
|
|
|
|
static string divisionFilter = "\"crop = trunc(iw / 2) * 2:trunc(ih / 2) * 2\"";
|
|
|
|
|
|
static string pngComprArg = "-compression_level 3";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
|
2020-11-25 12:40:17 +01:00
|
|
|
|
static string mpDecDef = "\"mpdecimate\"";
|
|
|
|
|
|
static string mpDecAggr = "\"mpdecimate=hi=64*32:lo=64*32:frac=0.1\"";
|
|
|
|
|
|
|
2020-11-26 20:17:18 +01:00
|
|
|
|
public static async Task ExtractSceneChanges(string inputFile, string frameFolderPath)
|
|
|
|
|
|
{
|
2020-12-07 12:34:12 +01:00
|
|
|
|
Logger.Log("Extracting scene changes...");
|
2021-01-03 22:37:06 +01:00
|
|
|
|
await VideoToFrames(inputFile, frameFolderPath, false, false, new Size(320, 180), false, true);
|
2020-12-07 12:34:12 +01:00
|
|
|
|
bool hiddenLog = Interpolate.currentInputFrameCount <= 50;
|
|
|
|
|
|
Logger.Log($"Detected {IOUtils.GetAmountOfFiles(frameFolderPath, false)} scene changes.".Replace(" 0 ", " no "), false, !hiddenLog);
|
2020-11-26 20:17:18 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-25 20:31:21 +01:00
|
|
|
|
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, bool timecodes = true)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2020-11-25 20:31:21 +01:00
|
|
|
|
await VideoToFrames(inputFile, frameFolderPath, deDupe, delSrc, new Size(), timecodes);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-03 22:37:06 +01:00
|
|
|
|
public static async Task VideoToFrames(string inputFile, string frameFolderPath, bool deDupe, bool delSrc, Size size, bool timecodes, bool sceneDetect = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2020-12-15 14:46:33 +01:00
|
|
|
|
if (!sceneDetect) Logger.Log("Extracting video frames from input video...");
|
2020-11-25 20:31:21 +01:00
|
|
|
|
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
2020-12-02 14:27:41 +01:00
|
|
|
|
IOUtils.CreateDir(frameFolderPath);
|
2021-01-03 21:00:32 +01:00
|
|
|
|
string timecodeStr = timecodes ? $"-copyts -r {FrameTiming.timebase} -frame_pts true" : "-copyts -frame_pts true";
|
2020-12-17 23:26:25 +01:00
|
|
|
|
string scnDetect = sceneDetect ? $"\"select='gt(scene,{Config.GetFloatString("scnDetectValue")})'\"" : "";
|
2020-12-15 14:46:33 +01:00
|
|
|
|
string mpStr = deDupe ? ((Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr) : "";
|
2020-12-29 16:01:24 +01:00
|
|
|
|
string fpsFilter = $"\"fps=fps={Interpolate.current.inFps.ToString().Replace(",", ".")}\"";
|
2021-01-03 21:00:32 +01:00
|
|
|
|
string filters = FormatUtils.ConcatStrings(new string[] { scnDetect, mpStr/*, fpsFilter*/ } );
|
2020-12-29 16:01:24 +01:00
|
|
|
|
string vf = filters.Length > 2 ? $"-vf {filters}" : "";
|
2020-12-02 17:23:24 +01:00
|
|
|
|
string pad = Padding.inputFrames.ToString();
|
2020-12-15 14:46:33 +01:00
|
|
|
|
string args = $"-i {inputFile.Wrap()} {pngComprArg} -vsync 0 -pix_fmt rgb24 {timecodeStr} {vf} {sizeStr} \"{frameFolderPath}/%{pad}d.png\"";
|
2020-12-07 12:34:12 +01:00
|
|
|
|
AvProcess.LogMode logMode = Interpolate.currentInputFrameCount > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
|
|
|
|
|
|
await AvProcess.RunFfmpeg(args, logMode);
|
2020-12-21 15:03:31 +01:00
|
|
|
|
if (!sceneDetect) Logger.Log($"Extracted {IOUtils.GetAmountOfFiles(frameFolderPath, false, "*.png")} frames from input.", false, true);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
await Task.Delay(1);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task ImportImages(string inpath, string outpath, bool delSrc = false, bool showLog = true)
|
2020-12-02 14:27:41 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (showLog) Logger.Log("Importing images...");
|
2020-12-18 00:19:08 +01:00
|
|
|
|
Logger.Log($"Importing images from {inpath} to {outpath}.");
|
2020-12-02 14:27:41 +01:00
|
|
|
|
IOUtils.CreateDir(outpath);
|
|
|
|
|
|
string concatFile = Path.Combine(Paths.GetDataPath(), "png-concat-temp.ini");
|
|
|
|
|
|
string concatFileContent = "";
|
2020-12-22 19:52:37 +01:00
|
|
|
|
foreach (string img in IOUtils.GetFilesSorted(inpath))
|
2020-12-02 14:27:41 +01:00
|
|
|
|
concatFileContent += $"file '{img.Replace(@"\", "/")}'\n";
|
|
|
|
|
|
File.WriteAllText(concatFile, concatFileContent);
|
|
|
|
|
|
|
|
|
|
|
|
string args = $" -loglevel panic -f concat -safe 0 -i {concatFile.Wrap()} {pngComprArg} -pix_fmt rgb24 -vsync 0 -vf {divisionFilter} \"{outpath}/%{Padding.inputFrames}d.png\"";
|
|
|
|
|
|
AvProcess.LogMode logMode = IOUtils.GetAmountOfFiles(inpath, false) > 50 ? AvProcess.LogMode.OnlyLastLine : AvProcess.LogMode.Hidden;
|
|
|
|
|
|
await AvProcess.RunFfmpeg(args, logMode);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inpath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-27 14:35:32 +01:00
|
|
|
|
public static async Task ExtractSingleFrame(string inputFile, int frameNum, bool hdr, bool delSrc)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2020-11-27 14:35:32 +01:00
|
|
|
|
string outPath = $"{inputFile}-frame{frameNum}.png";
|
|
|
|
|
|
await ExtractSingleFrame(inputFile, outPath, frameNum, hdr, delSrc);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task ExtractSingleFrame(string inputFile, string outputPath, int frameNum, bool hdr, bool delSrc)
|
|
|
|
|
|
{
|
|
|
|
|
|
string hdrStr = hdr ? hdrFilter : "";
|
2020-12-01 16:54:12 +01:00
|
|
|
|
string args = $"-i {inputFile.Wrap()} {pngComprArg} {hdrStr }-vf \"select=eq(n\\,{frameNum})\" -vframes 1 {outputPath.Wrap()}";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-02 16:20:21 +01:00
|
|
|
|
public static async Task FramesToVideoConcat(string framesFile, string outPath, Interpolate.OutMode outMode, float fps, AvProcess.LogMode logMode = AvProcess.LogMode.OnlyLastLine, bool isChunk = false)
|
2020-12-23 16:13:04 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (logMode != AvProcess.LogMode.Hidden)
|
|
|
|
|
|
Logger.Log($"Encoding video...");
|
2020-12-27 22:52:14 +01:00
|
|
|
|
string encArgs = Utils.GetEncArgs(Utils.GetCodec(outMode)) + " -pix_fmt yuv420p ";
|
|
|
|
|
|
if (!isChunk) encArgs += $"-movflags +faststart";
|
2020-12-23 16:13:04 +01:00
|
|
|
|
string vfrFilename = Path.GetFileName(framesFile);
|
2021-01-02 16:20:21 +01:00
|
|
|
|
//string vsync = (Interpolate.current.interpFactor == 2) ? "-vsync 1" : "-vsync 2";
|
2020-12-23 16:13:04 +01:00
|
|
|
|
string rate = fps.ToString().Replace(",", ".");
|
|
|
|
|
|
string extraArgs = Config.Get("ffEncArgs");
|
2021-01-02 16:20:21 +01:00
|
|
|
|
string args = $"-loglevel error -vsync 0 -f concat -r {rate} -i {vfrFilename} {encArgs} {extraArgs} -threads {Config.GetInt("ffEncThreads")} {outPath.Wrap()}";
|
2020-12-27 22:52:14 +01:00
|
|
|
|
//string args = $"-vsync 0 -f concat -i {vfrFilename} {encArgs} {extraArgs} -threads {Config.GetInt("ffEncThreads")} {outPath.Wrap()}";
|
2020-12-15 14:46:33 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), logMode);
|
2020-11-25 12:40:17 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task ConcatVideos(string concatFile, string outPath, float fps, int looptimes = -1)
|
2020-11-30 02:14:04 +01:00
|
|
|
|
{
|
|
|
|
|
|
Logger.Log($"Merging videos...");
|
|
|
|
|
|
string loopStr = (looptimes > 0) ? $"-stream_loop {looptimes}" : "";
|
2020-11-30 20:32:33 +01:00
|
|
|
|
string vfrFilename = Path.GetFileName(concatFile);
|
2020-12-27 22:52:14 +01:00
|
|
|
|
string args = $" {loopStr} -vsync 1 -f concat -r {fps.ToString().Replace(",", ".")} -i {vfrFilename} -c copy -pix_fmt yuv420p {outPath.Wrap()}";
|
2020-11-30 20:32:33 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, concatFile.GetParentDir(), AvProcess.LogMode.Hidden);
|
2020-11-30 02:14:04 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task ConvertFramerate(string inputPath, string outPath, bool useH265, int crf, float newFps, bool delSrc = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
Logger.Log($"Changing video frame rate...");
|
2020-11-25 17:48:27 +01:00
|
|
|
|
string enc = useH265 ? "libx265" : "libx264";
|
|
|
|
|
|
string presetStr = $"-preset {Config.Get("ffEncPreset")}";
|
2020-12-27 22:52:14 +01:00
|
|
|
|
string args = $" -i {inputPath.Wrap()} -filter:v fps=fps={newFps} -c:v {enc} -crf {crf} {presetStr} {videoEncArgs} {outPath.Wrap()}";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputPath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async void FramesToGif(string inputDir, bool palette, int fps, string prefix, bool delSrc = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2020-12-22 19:52:37 +01:00
|
|
|
|
int nums = IOUtils.GetFilenameCounterLength(IOUtils.GetFilesSorted(inputDir, false, "*.png")[0], prefix);
|
2020-12-08 14:43:03 +01:00
|
|
|
|
string filter = palette ? "-vf \"split[s0][s1];[s0]palettegen[p];[s1][p]paletteuse\"" : "";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
string args = "-framerate " + fps + " -i \"" + inputDir + "\\" + prefix + "%0" + nums + "d.png\" -f gif " + filter + " \"" + inputDir + ".gif\"";
|
|
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputDir);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-08 15:22:46 +01:00
|
|
|
|
public static async Task FramesToGifVfr(string framesFile, string outPath, bool palette, int colors = 64)
|
2020-12-08 14:43:03 +01:00
|
|
|
|
{
|
|
|
|
|
|
Logger.Log($"Encoding GIF...");
|
|
|
|
|
|
string vfrFilename = Path.GetFileName(framesFile);
|
2020-12-08 15:22:46 +01:00
|
|
|
|
string filter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither=floyd_steinberg:diff_mode=rectangle\"" : "";
|
2020-12-08 14:43:03 +01:00
|
|
|
|
string args = $"-f concat -i {vfrFilename.Wrap()} -f gif {filter} {outPath.Wrap()}";
|
|
|
|
|
|
await AvProcess.RunFfmpeg(args, framesFile.GetParentDir(), AvProcess.LogMode.OnlyLastLine);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task LoopVideo(string inputFile, int times, bool delSrc = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
string pathNoExt = Path.ChangeExtension(inputFile, null);
|
|
|
|
|
|
string ext = Path.GetExtension(inputFile);
|
2020-11-26 21:18:31 +01:00
|
|
|
|
string args = $" -stream_loop {times} -i {inputFile.Wrap()} -c copy \"{pathNoExt}-Loop{times}{ext}\"";
|
2020-12-03 00:04:46 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task LoopVideoEnc(string inputFile, int times, bool useH265, int crf, bool delSrc = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
string pathNoExt = Path.ChangeExtension(inputFile, null);
|
|
|
|
|
|
string ext = Path.GetExtension(inputFile);
|
|
|
|
|
|
string enc = "libx264";
|
|
|
|
|
|
if (useH265) enc = "libx265";
|
2020-12-15 14:46:33 +01:00
|
|
|
|
string args = " -stream_loop " + times + " -i \"" + inputFile + "\" -c:v " + enc + " -crf " + crf + " -c:a copy \"" + pathNoExt + "-" + times + "xLoop" + ext + "\"";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task ChangeSpeed(string inputFile, float newSpeedPercent, bool delSrc = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
string pathNoExt = Path.ChangeExtension(inputFile, null);
|
|
|
|
|
|
string ext = Path.GetExtension(inputFile);
|
|
|
|
|
|
float val = newSpeedPercent / 100f;
|
|
|
|
|
|
string speedVal = (1f / val).ToString("0.0000").Replace(",", ".");
|
|
|
|
|
|
string args = " -itsscale " + speedVal + " -i \"" + inputFile + "\" -c copy \"" + pathNoExt + "-" + newSpeedPercent + "pcSpeed" + ext + "\"";
|
|
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task Encode(string inputFile, string vcodec, string acodec, int crf, int audioKbps = 0, bool delSrc = false)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
string outPath = Path.ChangeExtension(inputFile, null) + "-convert.mp4";
|
2021-01-02 16:20:21 +01:00
|
|
|
|
string args = $" -i {inputFile.Wrap()} -c:v {vcodec} -crf {crf} -pix_fmt yuv420p -c:a {acodec} -b:a {audioKbps}k -vf {divisionFilter} {outPath.Wrap()}";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
if (string.IsNullOrWhiteSpace(acodec))
|
|
|
|
|
|
args = args.Replace("-c:a", "-an");
|
2020-12-15 14:46:33 +01:00
|
|
|
|
if (audioKbps < 0)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
args = args.Replace($" -b:a {audioKbps}", "");
|
|
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.OnlyLastLine);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static async Task ExtractAudio(string inputFile, string outFile) // https://stackoverflow.com/a/27413824/14274419
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
Logger.Log($"[FFCmds] Extracting audio from {inputFile} to {outFile}", true);
|
2020-12-23 18:21:31 +01:00
|
|
|
|
//string ext = GetAudioExt(inputFile);
|
|
|
|
|
|
outFile = Path.ChangeExtension(outFile, ".ogg");
|
2020-12-24 12:31:16 +01:00
|
|
|
|
string args = $" -loglevel panic -i {inputFile.Wrap()} -vn -acodec libopus -b:a 256k {outFile.Wrap()}";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
|
|
|
|
|
|
if (AvProcess.lastOutputFfmpeg.ToLower().Contains("error") && File.Exists(outFile)) // If broken file was written
|
|
|
|
|
|
File.Delete(outFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task MergeAudio(string inputFile, string audioPath, int looptimes = -1) // https://superuser.com/a/277667
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Log($"[FFCmds] Merging audio from {audioPath} into {inputFile}", true);
|
2020-12-23 18:21:31 +01:00
|
|
|
|
string tempPath = inputFile + "-temp" + Path.GetExtension(inputFile);
|
|
|
|
|
|
// if (Path.GetExtension(audioPath) == ".wav")
|
|
|
|
|
|
// {
|
|
|
|
|
|
// Logger.Log("Using MKV instead of MP4 to enable support for raw audio.");
|
|
|
|
|
|
// tempPath = Path.ChangeExtension(tempPath, "mkv");
|
|
|
|
|
|
// }
|
|
|
|
|
|
string aCodec = Utils.GetAudioEnc(Utils.GetCodec(Interpolate.current.outMode));
|
2020-12-24 12:31:16 +01:00
|
|
|
|
int aKbits = Utils.GetAudioKbits(aCodec);
|
2020-12-23 18:21:31 +01:00
|
|
|
|
string args = $" -i {inputFile.Wrap()} -stream_loop {looptimes} -i {audioPath.Wrap()} -shortest -c:v copy -c:a {aCodec} -b:a {aKbits}k {tempPath.Wrap()}";
|
2020-11-23 16:51:05 +01:00
|
|
|
|
await AvProcess.RunFfmpeg(args, AvProcess.LogMode.Hidden);
|
2020-12-15 14:46:33 +01:00
|
|
|
|
if (AvProcess.lastOutputFfmpeg.Contains("Invalid data"))
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
Logger.Log("Failed to merge audio!");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2020-12-07 12:34:12 +01:00
|
|
|
|
string movePath = Path.ChangeExtension(inputFile, Path.GetExtension(tempPath));
|
|
|
|
|
|
File.Delete(movePath);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
File.Delete(inputFile);
|
2020-12-07 12:34:12 +01:00
|
|
|
|
File.Move(tempPath, movePath);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static float GetFramerate(string inputFile)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2020-12-27 22:52:14 +01:00
|
|
|
|
Logger.Log("Reading FPS using ffmpeg.", true, false, "ffmpeg");
|
2020-11-23 16:51:05 +01:00
|
|
|
|
string args = $" -i {inputFile.Wrap()}";
|
2020-11-23 18:08:17 +01:00
|
|
|
|
string output = AvProcess.GetFfmpegOutput(args);
|
|
|
|
|
|
string[] entries = output.Split(',');
|
2020-12-15 14:46:33 +01:00
|
|
|
|
foreach (string entry in entries)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2020-11-23 18:08:17 +01:00
|
|
|
|
if (entry.Contains(" fps") && !entry.Contains("Input ")) // Avoid reading FPS from the filename, in case filename contains "fps"
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
Logger.Log("[FFCmds] FPS Entry: " + entry, true);
|
|
|
|
|
|
string num = entry.Replace(" fps", "").Trim().Replace(",", ".");
|
|
|
|
|
|
float value;
|
|
|
|
|
|
float.TryParse(num, NumberStyles.Any, CultureInfo.InvariantCulture, out value);
|
|
|
|
|
|
return value;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
public static Size GetSize(string inputFile)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
string args = $" -v error -select_streams v:0 -show_entries stream=width,height -of csv=s=x:p=0 {inputFile.Wrap()}";
|
|
|
|
|
|
string output = AvProcess.GetFfprobeOutput(args);
|
|
|
|
|
|
|
|
|
|
|
|
if (output.Length > 4 && output.Contains("x"))
|
|
|
|
|
|
{
|
|
|
|
|
|
string[] numbers = output.Split('x');
|
|
|
|
|
|
return new Size(numbers[0].GetInt(), numbers[1].GetInt());
|
|
|
|
|
|
}
|
|
|
|
|
|
return new Size(0, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static int GetFrameCount(string inputFile)
|
|
|
|
|
|
{
|
|
|
|
|
|
int frames = 0;
|
|
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
Logger.Log("Reading frame count using ffprobe.", true, false, "ffmpeg");
|
2020-11-23 16:51:05 +01:00
|
|
|
|
frames = ReadFrameCountFfprobe(inputFile, Config.GetBool("ffprobeCountFrames")); // Try reading frame count with ffprobe
|
|
|
|
|
|
if (frames > 0)
|
|
|
|
|
|
return frames;
|
|
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
Logger.Log($"Failed to get frame count using ffprobe (frames = {frames}). Reading frame count using ffmpeg.", true, false, "ffmpeg");
|
2020-11-23 16:51:05 +01:00
|
|
|
|
frames = ReadFrameCountFfmpeg(inputFile); // Try reading frame count with ffmpeg
|
|
|
|
|
|
if (frames > 0)
|
|
|
|
|
|
return frames;
|
|
|
|
|
|
|
|
|
|
|
|
Logger.Log("Failed to get total frame count of video.");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
public static async Task<int> GetFrameCountAsync(string inputFile)
|
|
|
|
|
|
{
|
|
|
|
|
|
int frames = 0;
|
|
|
|
|
|
|
|
|
|
|
|
Logger.Log("Reading frame count using ffprobe.", true, false, "ffmpeg");
|
|
|
|
|
|
frames = await ReadFrameCountFfprobeAsync(inputFile, Config.GetBool("ffprobeCountFrames")); // Try reading frame count with ffprobe
|
|
|
|
|
|
if (frames > 0)
|
|
|
|
|
|
return frames;
|
|
|
|
|
|
|
|
|
|
|
|
Logger.Log($"Failed to get frame count using ffprobe (frames = {frames}). Reading frame count using ffmpeg.", true, false, "ffmpeg");
|
|
|
|
|
|
frames = await ReadFrameCountFfmpegAsync(inputFile); // Try reading frame count with ffmpeg
|
|
|
|
|
|
if (frames > 0)
|
|
|
|
|
|
return frames;
|
|
|
|
|
|
|
|
|
|
|
|
Logger.Log("Failed to get total frame count of video.");
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
static int ReadFrameCountFfprobe(string inputFile, bool readFramesSlow)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
string args = $" -v panic -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1 {inputFile.Wrap()}";
|
|
|
|
|
|
if (readFramesSlow)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Log("Counting total frames using FFprobe. This can take a moment...");
|
|
|
|
|
|
args = $" -v panic -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 {inputFile.Wrap()}";
|
|
|
|
|
|
}
|
|
|
|
|
|
string info = AvProcess.GetFfprobeOutput(args);
|
|
|
|
|
|
string[] entries = info.SplitIntoLines();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (readFramesSlow)
|
|
|
|
|
|
return info.GetInt();
|
|
|
|
|
|
foreach (string entry in entries)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entry.Contains("nb_frames="))
|
|
|
|
|
|
return entry.GetInt();
|
2020-12-27 22:52:14 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch { }
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static async Task<int> ReadFrameCountFfprobeAsync(string inputFile, bool readFramesSlow)
|
|
|
|
|
|
{
|
|
|
|
|
|
string args = $" -v panic -select_streams v:0 -show_entries stream=nb_frames -of default=noprint_wrappers=1 {inputFile.Wrap()}";
|
|
|
|
|
|
if (readFramesSlow)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Log("Counting total frames using FFprobe. This can take a moment...");
|
|
|
|
|
|
await Task.Delay(10);
|
|
|
|
|
|
args = $" -v panic -count_frames -select_streams v:0 -show_entries stream=nb_read_frames -of default=nokey=1:noprint_wrappers=1 {inputFile.Wrap()}";
|
|
|
|
|
|
}
|
|
|
|
|
|
string info = AvProcess.GetFfprobeOutput(args);
|
|
|
|
|
|
string[] entries = info.SplitIntoLines();
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
if (readFramesSlow)
|
|
|
|
|
|
return info.GetInt();
|
|
|
|
|
|
foreach (string entry in entries)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entry.Contains("nb_frames="))
|
|
|
|
|
|
return entry.GetInt();
|
2020-11-23 16:51:05 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch { }
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
static int ReadFrameCountFfmpeg(string inputFile)
|
|
|
|
|
|
{
|
|
|
|
|
|
string args = $" -loglevel panic -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null - ";
|
|
|
|
|
|
string info = AvProcess.GetFfmpegOutput(args);
|
|
|
|
|
|
string[] entries = info.SplitIntoLines();
|
|
|
|
|
|
foreach (string entry in entries)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entry.Contains("frame="))
|
|
|
|
|
|
return entry.Substring(0, entry.IndexOf("fps")).GetInt();
|
|
|
|
|
|
}
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-27 22:52:14 +01:00
|
|
|
|
static async Task<int> ReadFrameCountFfmpegAsync (string inputFile)
|
|
|
|
|
|
{
|
|
|
|
|
|
string args = $" -loglevel panic -i {inputFile.Wrap()} -map 0:v:0 -c copy -f null - ";
|
|
|
|
|
|
string info = await AvProcess.GetFfmpegOutputAsync(args, true);
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
|
|
|
|
|
string[] lines = info.SplitIntoLines();
|
|
|
|
|
|
string lastLine = lines.Last();
|
|
|
|
|
|
return lastLine.Substring(0, lastLine.IndexOf("fps")).GetInt();
|
|
|
|
|
|
}
|
|
|
|
|
|
catch
|
|
|
|
|
|
{
|
|
|
|
|
|
return -1;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-25 00:39:14 +01:00
|
|
|
|
public static string GetAudioCodec(string path)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
string args = $" -v panic -show_streams -select_streams a -show_entries stream=codec_name {path.Wrap()}";
|
|
|
|
|
|
string info = AvProcess.GetFfprobeOutput(args);
|
|
|
|
|
|
string[] entries = info.SplitIntoLines();
|
|
|
|
|
|
foreach (string entry in entries)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (entry.Contains("codec_name="))
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Log($"[FFCmds] Audio Codec Entry: {entry}", true);
|
|
|
|
|
|
return entry.Split('=')[1];
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
static string GetFirstStreamInfo(string ffmpegOutput)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
|
|
|
|
|
foreach (string line in Regex.Split(ffmpegOutput, "\r\n|\r|\n"))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (line.Contains("Stream #0"))
|
|
|
|
|
|
return line;
|
|
|
|
|
|
}
|
|
|
|
|
|
return "";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-12-15 14:46:33 +01:00
|
|
|
|
static void DeleteSource(string path)
|
2020-11-23 16:51:05 +01:00
|
|
|
|
{
|
2020-11-30 02:14:04 +01:00
|
|
|
|
Logger.Log("[FFCmds] Deleting input file/dir: " + path, true);
|
2020-11-23 16:51:05 +01:00
|
|
|
|
|
|
|
|
|
|
if (IOUtils.IsPathDirectory(path) && Directory.Exists(path))
|
|
|
|
|
|
Directory.Delete(path, true);
|
|
|
|
|
|
|
|
|
|
|
|
if (!IOUtils.IsPathDirectory(path) && File.Exists(path))
|
|
|
|
|
|
File.Delete(path);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|