2021-02-01 16:23:35 +01:00
|
|
|
|
using Flowframes.Data;
|
|
|
|
|
|
using Flowframes.IO;
|
|
|
|
|
|
using Flowframes.Main;
|
|
|
|
|
|
using Flowframes.MiscUtils;
|
2021-02-23 12:13:30 +01:00
|
|
|
|
using Flowframes.UI;
|
2021-02-01 16:23:35 +01:00
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Drawing;
|
|
|
|
|
|
using System.Globalization;
|
|
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Threading.Tasks;
|
|
|
|
|
|
using static Flowframes.AvProcess;
|
|
|
|
|
|
|
2021-02-02 12:56:48 +01:00
|
|
|
|
namespace Flowframes.Media
|
2021-02-01 16:23:35 +01:00
|
|
|
|
{
|
2021-02-02 12:56:48 +01:00
|
|
|
|
partial class FfmpegExtract : FfmpegCommands
|
2021-02-01 16:23:35 +01:00
|
|
|
|
{
|
|
|
|
|
|
public static async Task ExtractSceneChanges(string inputFile, string frameFolderPath, float rate)
|
|
|
|
|
|
{
|
|
|
|
|
|
Logger.Log("Extracting scene changes...");
|
2021-02-08 11:03:17 +01:00
|
|
|
|
await VideoToFrames(inputFile, frameFolderPath, false, rate, false, false, new Size(320, 180), true);
|
2021-02-01 16:23:35 +01:00
|
|
|
|
bool hiddenLog = Interpolate.currentInputFrameCount <= 50;
|
|
|
|
|
|
int amount = IOUtils.GetAmountOfFiles(frameFolderPath, false);
|
|
|
|
|
|
Logger.Log($"Detected {amount} scene {(amount == 1 ? "change" : "changes")}.".Replace(" 0 ", " no "), false, !hiddenLog);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-08 11:03:17 +01:00
|
|
|
|
public static async Task VideoToFrames(string inputFile, string framesDir, bool alpha, float rate, bool deDupe, bool delSrc)
|
2021-02-01 16:23:35 +01:00
|
|
|
|
{
|
2021-02-08 11:03:17 +01:00
|
|
|
|
await VideoToFrames(inputFile, framesDir, alpha, rate, deDupe, delSrc, new Size());
|
2021-02-01 16:23:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-08 11:03:17 +01:00
|
|
|
|
public static async Task VideoToFrames(string inputFile, string framesDir, bool alpha, float rate, bool deDupe, bool delSrc, Size size, bool sceneDetect = false)
|
2021-02-01 16:23:35 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (!sceneDetect) Logger.Log("Extracting video frames from input video...");
|
|
|
|
|
|
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
|
|
|
|
|
IOUtils.CreateDir(framesDir);
|
2021-03-01 22:38:38 +01:00
|
|
|
|
string timecodeStr = /* timecodes ? $"-copyts -r {FrameOrder.timebase} -frame_pts true" : */ "-frame_pts true";
|
2021-02-01 16:23:35 +01:00
|
|
|
|
string scnDetect = sceneDetect ? $"\"select='gt(scene,{Config.GetFloatString("scnDetectValue")})'\"" : "";
|
|
|
|
|
|
string mpStr = deDupe ? ((Config.GetInt("mpdecimateMode") == 0) ? mpDecDef : mpDecAggr) : "";
|
|
|
|
|
|
string filters = FormatUtils.ConcatStrings(new string[] { divisionFilter, scnDetect, mpStr });
|
|
|
|
|
|
string vf = filters.Length > 2 ? $"-vf {filters}" : "";
|
|
|
|
|
|
string rateArg = (rate > 0) ? $" -r {rate.ToStringDot()}" : "";
|
|
|
|
|
|
string pixFmt = alpha ? "-pix_fmt rgba" : "-pix_fmt rgb24"; // Use RGBA for GIF for alpha support
|
2021-03-01 22:38:38 +01:00
|
|
|
|
string args = $"{rateArg} {GetTrimArg(true)} -i {inputFile.Wrap()} {compr} -vsync 0 {pixFmt} {timecodeStr} {vf} {sizeStr} {GetTrimArg(false)} \"{framesDir}/%{Padding.inputFrames}d.png\"";
|
2021-02-01 16:23:35 +01:00
|
|
|
|
LogMode logMode = Interpolate.currentInputFrameCount > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
|
2021-02-23 12:13:30 +01:00
|
|
|
|
await RunFfmpeg(args, logMode, TaskType.ExtractFrames, true);
|
2021-02-01 16:23:35 +01:00
|
|
|
|
int amount = IOUtils.GetAmountOfFiles(framesDir, false, "*.png");
|
|
|
|
|
|
if (!sceneDetect) Logger.Log($"Extracted {amount} {(amount == 1 ? "frame" : "frames")} from input.", false, true);
|
|
|
|
|
|
await Task.Delay(1);
|
|
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inputFile);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task ImportImages(string inpath, string outpath, bool alpha, Size size, bool delSrc = false, bool showLog = true)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (showLog) Logger.Log("Importing images...");
|
|
|
|
|
|
Logger.Log($"Importing images from {inpath} to {outpath}.", true);
|
|
|
|
|
|
IOUtils.CreateDir(outpath);
|
|
|
|
|
|
string concatFile = Path.Combine(Paths.GetDataPath(), "png-concat-temp.ini");
|
|
|
|
|
|
string concatFileContent = "";
|
|
|
|
|
|
string[] files = IOUtils.GetFilesSorted(inpath);
|
|
|
|
|
|
foreach (string img in files)
|
|
|
|
|
|
concatFileContent += $"file '{img.Replace(@"\", "/")}'\n";
|
|
|
|
|
|
File.WriteAllText(concatFile, concatFileContent);
|
|
|
|
|
|
|
|
|
|
|
|
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
|
|
|
|
|
string pixFmt = alpha ? "-pix_fmt rgba" : "-pix_fmt rgb24"; // Use RGBA for GIF for alpha support
|
2021-02-04 22:49:19 +01:00
|
|
|
|
string vf = alpha ? $"-filter_complex \"[0:v]{divisionFilter},split[a][b];[a]palettegen=reserve_transparent=on:transparency_color=ffffff[p];[b][p]paletteuse\"" : $"-vf {divisionFilter}";
|
2021-03-01 22:38:38 +01:00
|
|
|
|
string args = $" -loglevel panic -f concat -safe 0 -i {concatFile.Wrap()} {compr} {sizeStr} {pixFmt} -vsync 0 {vf} \"{outpath}/%{Padding.inputFrames}d.png\"";
|
2021-02-01 16:23:35 +01:00
|
|
|
|
LogMode logMode = IOUtils.GetAmountOfFiles(inpath, false) > 50 ? LogMode.OnlyLastLine : LogMode.Hidden;
|
2021-02-23 12:13:30 +01:00
|
|
|
|
await RunFfmpeg(args, logMode, TaskType.ExtractFrames);
|
2021-02-01 16:23:35 +01:00
|
|
|
|
if (delSrc)
|
|
|
|
|
|
DeleteSource(inpath);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-03-01 22:38:38 +01:00
|
|
|
|
static string GetTrimArg (bool input)
|
2021-02-23 12:13:30 +01:00
|
|
|
|
{
|
|
|
|
|
|
if (!QuickSettingsTab.trimEnabled)
|
|
|
|
|
|
return "";
|
|
|
|
|
|
|
2021-03-01 22:38:38 +01:00
|
|
|
|
int fastSeekThresh = 180;
|
|
|
|
|
|
bool fastSeek = QuickSettingsTab.trimStartSecs > fastSeekThresh;
|
|
|
|
|
|
string arg = "";
|
2021-02-28 16:26:46 +01:00
|
|
|
|
|
2021-03-01 22:38:38 +01:00
|
|
|
|
if (input)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (fastSeek)
|
|
|
|
|
|
arg += $"-ss {QuickSettingsTab.trimStartSecs - fastSeekThresh}";
|
|
|
|
|
|
else
|
|
|
|
|
|
return arg;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
if(fastSeek)
|
|
|
|
|
|
{
|
|
|
|
|
|
arg += $"-ss {fastSeekThresh}";
|
|
|
|
|
|
|
|
|
|
|
|
long trimTimeSecs = QuickSettingsTab.trimEndSecs - QuickSettingsTab.trimStartSecs;
|
|
|
|
|
|
|
|
|
|
|
|
if (QuickSettingsTab.doTrimEnd)
|
|
|
|
|
|
arg += $" -to {fastSeekThresh + trimTimeSecs}";
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
arg += $"-ss {QuickSettingsTab.trimStart}";
|
|
|
|
|
|
|
|
|
|
|
|
if (QuickSettingsTab.doTrimEnd)
|
|
|
|
|
|
arg += $" -to {QuickSettingsTab.trimEnd}";
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2021-02-28 16:26:46 +01:00
|
|
|
|
|
|
|
|
|
|
return arg;
|
2021-02-23 12:13:30 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2021-02-01 16:23:35 +01:00
|
|
|
|
public static async Task ImportSingleImage(string inputFile, string outPath, Size size)
|
|
|
|
|
|
{
|
|
|
|
|
|
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
|
|
|
|
|
bool isPng = (Path.GetExtension(outPath).ToLower() == ".png");
|
2021-03-01 22:38:38 +01:00
|
|
|
|
string comprArg = isPng ? compr : "";
|
2021-02-01 16:23:35 +01:00
|
|
|
|
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
|
|
|
|
|
|
string args = $"-i {inputFile.Wrap()} {comprArg} {sizeStr} {pixFmt} -vf {divisionFilter} {outPath.Wrap()}";
|
2021-02-23 12:13:30 +01:00
|
|
|
|
await RunFfmpeg(args, LogMode.Hidden, TaskType.ExtractFrames);
|
2021-02-01 16:23:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task ExtractSingleFrame(string inputFile, string outputPath, int frameNum)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool isPng = (Path.GetExtension(outputPath).ToLower() == ".png");
|
2021-03-01 22:38:38 +01:00
|
|
|
|
string comprArg = isPng ? compr : "";
|
2021-02-01 16:23:35 +01:00
|
|
|
|
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
|
|
|
|
|
|
string args = $"-i {inputFile.Wrap()} -vf \"select=eq(n\\,{frameNum})\" -vframes 1 {pixFmt} {outputPath.Wrap()}";
|
2021-02-23 12:13:30 +01:00
|
|
|
|
await RunFfmpeg(args, LogMode.Hidden, TaskType.ExtractFrames);
|
2021-02-01 16:23:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static async Task ExtractLastFrame(string inputFile, string outputPath, Size size)
|
|
|
|
|
|
{
|
2021-02-23 12:13:30 +01:00
|
|
|
|
if (QuickSettingsTab.trimEnabled)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2021-02-01 16:23:35 +01:00
|
|
|
|
if (IOUtils.IsPathDirectory(outputPath))
|
|
|
|
|
|
outputPath = Path.Combine(outputPath, "last.png");
|
2021-02-23 12:13:30 +01:00
|
|
|
|
|
2021-02-01 16:23:35 +01:00
|
|
|
|
bool isPng = (Path.GetExtension(outputPath).ToLower() == ".png");
|
2021-03-01 22:38:38 +01:00
|
|
|
|
string comprArg = isPng ? compr : "";
|
2021-02-01 16:23:35 +01:00
|
|
|
|
string pixFmt = "-pix_fmt " + (isPng ? $"rgb24 {comprArg}" : "yuvj420p");
|
|
|
|
|
|
string sizeStr = (size.Width > 1 && size.Height > 1) ? $"-s {size.Width}x{size.Height}" : "";
|
2021-02-23 12:13:30 +01:00
|
|
|
|
string trim = QuickSettingsTab.trimEnabled ? $"-ss {QuickSettingsTab.GetTrimEndMinusOne()} -to {QuickSettingsTab.trimEnd}" : "";
|
|
|
|
|
|
string sseof = string.IsNullOrWhiteSpace(trim) ? "-sseof -1" : "";
|
|
|
|
|
|
string args = $"{sseof} -i {inputFile.Wrap()} -update 1 {pixFmt} {sizeStr} {trim} {outputPath.Wrap()}";
|
|
|
|
|
|
await RunFfmpeg(args, LogMode.Hidden, TaskType.ExtractFrames);
|
2021-02-01 16:23:35 +01:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|