FfmpegCommands refactoring, renamed AudioVideo namespace

This commit is contained in:
N00MKRAD
2021-02-02 12:56:48 +01:00
parent 832f286367
commit 2bdda6522f
18 changed files with 212 additions and 185 deletions

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Main;

View File

@@ -200,6 +200,7 @@
</Reference>
</ItemGroup>
<ItemGroup>
<Compile Include="AudioVideo\FfmpegAudioAndMetadata.cs" />
<Compile Include="AudioVideo\FfmpegEncode.cs" />
<Compile Include="AudioVideo\FfmpegExtract.cs" />
<Compile Include="AudioVideo\FFmpegUtils.cs" />

View File

@@ -313,7 +313,7 @@ namespace Flowframes.IO
Logger.Log("Failed to read FPS - Trying alternative method...", true);
try
{
fps = FFmpegCommands.GetFramerate(path);
fps = FfmpegCommands.GetFramerate(path);
Logger.Log("Detected FPS of " + Path.GetFileName(path) + " as " + fps + " FPS", true);
}
catch
@@ -377,7 +377,7 @@ namespace Flowframes.IO
Logger.Log($"Failed to read video size ({e.Message}) - Trying alternative method...", true);
try
{
size = FFmpegCommands.GetSize(path);
size = FfmpegCommands.GetSize(path);
Logger.Log($"Detected video size of {Path.GetFileName(path)} as {size.Width}x{size.Height}", true);
}
catch

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.MiscUtils;

View File

@@ -13,7 +13,7 @@ using System.Windows.Forms;
using Padding = Flowframes.Data.Padding;
using i = Flowframes.Interpolate;
using System.Diagnostics;
using Flowframes.AudioVideo;
using Flowframes.Media;
namespace Flowframes.Main
{
@@ -104,7 +104,7 @@ namespace Flowframes.Main
if (mode == i.OutMode.VidGif)
{
await FFmpegCommands.FramesToGifConcat(vfrFile, outPath, fps, true, Config.GetInt("gifColors"), resampleFps);
await FfmpegEncode.FramesToGifConcat(vfrFile, outPath, fps, true, Config.GetInt("gifColors"), resampleFps);
}
else
{
@@ -149,7 +149,7 @@ namespace Flowframes.Main
static async Task MergeChunks(string vfrFile, string outPath)
{
await FFmpegCommands.ConcatVideos(vfrFile, outPath, -1);
await FfmpegCommands.ConcatVideos(vfrFile, outPath, -1);
await MergeAudio(i.current.inPath, outPath);
await Loop(outPath, GetLoopTimes());
}
@@ -181,7 +181,7 @@ namespace Flowframes.Main
{
if (looptimes < 1 || !Config.GetBool("enableLoop")) return;
Logger.Log($"Looping {looptimes} {(looptimes == 1 ? "time" : "times")} to reach target length of {Config.GetInt("minOutVidLength")}s...");
await FFmpegCommands.LoopVideo(outPath, looptimes, Config.GetInt("loopMode") == 0);
await FfmpegCommands.LoopVideo(outPath, looptimes, Config.GetInt("loopMode") == 0);
}
static int GetLoopTimes()
@@ -208,7 +208,7 @@ namespace Flowframes.Main
audioFileBasePath = Path.Combine(i.current.tempFolder.GetParentDir(), "audio");
if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath)))
await FFmpegCommands.ExtractAudio(inputPath, audioFileBasePath); // Extract from sourceVideo to audioFile unless it already exists
await FfmpegAudioAndMetadata.ExtractAudio(inputPath, audioFileBasePath); // Extract from sourceVideo to audioFile unless it already exists
if (!File.Exists(IOUtils.GetAudioFile(audioFileBasePath)) || new FileInfo(IOUtils.GetAudioFile(audioFileBasePath)).Length < 4096)
{
@@ -216,7 +216,7 @@ namespace Flowframes.Main
return;
}
await FFmpegCommands.MergeAudioAndSubs(outVideo, IOUtils.GetAudioFile(audioFileBasePath), i.current.tempFolder); // Merge from audioFile into outVideo
await FfmpegAudioAndMetadata.MergeAudioAndSubs(outVideo, IOUtils.GetAudioFile(audioFileBasePath), i.current.tempFolder); // Merge from audioFile into outVideo
}
catch (Exception e)
{

View File

@@ -1,5 +1,5 @@
using Flowframes;
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Magick;
@@ -75,9 +75,9 @@ namespace Flowframes
{
Program.mainForm.SetStatus("Extracting transparency...");
Logger.Log("Extracting transparency... (1/2)");
await FFmpegCommands.ExtractAlphaDir(current.framesFolder, current.framesFolder + Paths.alphaSuffix);
await FfmpegCommands.ExtractAlphaDir(current.framesFolder, current.framesFolder + Paths.alphaSuffix);
Logger.Log("Extracting transparency... (2/2)", false, true);
await FFmpegCommands.RemoveAlpha(current.framesFolder, current.framesFolder);
await FfmpegCommands.RemoveAlpha(current.framesFolder, current.framesFolder);
}
}
@@ -110,10 +110,10 @@ namespace Flowframes
{
string audioFile = Path.Combine(current.tempFolder, "audio");
if (audioFile != null && !File.Exists(audioFile))
await FFmpegCommands.ExtractAudio(inPath, audioFile);
await FfmpegAudioAndMetadata.ExtractAudio(inPath, audioFile);
}
await FFmpegCommands.ExtractSubtitles(inPath, current.tempFolder, current.outMode);
await FfmpegAudioAndMetadata.ExtractSubtitles(inPath, current.tempFolder, current.outMode);
}
public static async Task PostProcessFrames (bool sbsMode = false)

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using System;

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.Forms;
using Flowframes.IO;
@@ -178,7 +178,7 @@ namespace Flowframes.Main
if (IOUtils.IsPathDirectory(path))
frameCount = IOUtils.GetAmountOfFiles(path, false);
else
frameCount = await FFmpegCommands.GetFrameCountAsync(path);
frameCount = await FfmpegCommands.GetFrameCountAsync(path);
if (hash.Length > 1 && frameCount > 5000) // Cache if >5k frames to avoid re-reading it every single time
{
@@ -456,7 +456,7 @@ namespace Flowframes.Main
public static void UpdateVideoDuration(string path)
{
Program.mainForm.currInDuration = FFmpegCommands.GetDuration(path);
Program.mainForm.currInDuration = FfmpegCommands.GetDuration(path);
}
}
}

View File

@@ -5,7 +5,7 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Flowframes.AudioVideo
namespace Flowframes.Media
{
class FFmpegUtils
{
@@ -123,7 +123,7 @@ namespace Flowframes.AudioVideo
public static string GetAudioExt(string videoFile)
{
switch (FFmpegCommands.GetAudioCodec(videoFile))
switch (FfmpegCommands.GetAudioCodec(videoFile))
{
case "vorbis": return "ogg";
case "opus": return "ogg";

View File

@@ -0,0 +1,148 @@
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Main;
using Flowframes.MiscUtils;
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;
using Utils = Flowframes.Media.FFmpegUtils;
namespace Flowframes.Media
{
partial class FfmpegAudioAndMetadata : FfmpegCommands
{
public static async Task ExtractAudio(string inputFile, string outFile) // https://stackoverflow.com/a/27413824/14274419
{
string audioExt = Utils.GetAudioExt(inputFile);
outFile = Path.ChangeExtension(outFile, audioExt);
Logger.Log($"[FFCmds] Extracting audio from {inputFile} to {outFile}", true);
string args = $" -loglevel panic -i {inputFile.Wrap()} -vn -c:a copy {outFile.Wrap()}";
await RunFfmpeg(args, LogMode.Hidden);
if (File.Exists(outFile) && IOUtils.GetFilesize(outFile) < 512)
{
Logger.Log("Failed to extract audio losslessly! Trying to re-encode.");
File.Delete(outFile);
outFile = Path.ChangeExtension(outFile, Utils.GetAudioExtForContainer(Path.GetExtension(inputFile)));
args = $" -loglevel panic -i {inputFile.Wrap()} -vn {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} {outFile.Wrap()}";
await RunFfmpeg(args, LogMode.Hidden);
if ((File.Exists(outFile) && IOUtils.GetFilesize(outFile) < 512) || lastOutputFfmpeg.Contains("Invalid data"))
{
Logger.Log("Failed to extract audio, even with re-encoding. Output will not have audio.");
IOUtils.TryDeleteIfExists(outFile);
return;
}
Logger.Log($"Source audio has been re-encoded as it can't be extracted losslessly. This may decrease the quality slightly.", false, true);
}
}
public static async Task ExtractSubtitles(string inputFile, string outFolder, Interpolate.OutMode outMode)
{
Dictionary<int, string> subtitleTracks = await GetSubtitleTracks(inputFile);
foreach (KeyValuePair<int, string> subTrack in subtitleTracks)
{
string trackName = subTrack.Value.Length > 4 ? CultureInfo.CurrentCulture.TextInfo.ToTitleCase(subTrack.Value.ToLower()) : subTrack.Value.ToUpper();
string outPath = Path.Combine(outFolder, $"{subTrack.Key}-{trackName}.srt");
string args = $" -loglevel error -i {inputFile.Wrap()} -map 0:s:{subTrack.Key} {outPath.Wrap()}";
await RunFfmpeg(args, LogMode.Hidden);
if (lastOutputFfmpeg.Contains("matches no streams")) // Break if there are no more subtitle tracks
break;
Logger.Log($"[FFCmds] Extracted subtitle track {subTrack.Key} to {outPath} ({FormatUtils.Bytes(IOUtils.GetFilesize(outPath))})", true, false, "ffmpeg");
}
if (subtitleTracks.Count > 0)
{
Logger.Log($"Extracted {subtitleTracks.Count} subtitle tracks from the input video.");
Utils.ContainerSupportsSubs(Utils.GetExt(outMode), true);
}
}
public static async Task<Dictionary<int, string>> GetSubtitleTracks(string inputFile)
{
Dictionary<int, string> subDict = new Dictionary<int, string>();
string args = $"-i {inputFile.Wrap()}";
string[] outputLines = (await GetFfmpegOutputAsync(args)).SplitIntoLines();
string[] filteredLines = outputLines.Where(l => l.Contains(" Subtitle: ")).ToArray();
int idx = 0;
foreach (string line in filteredLines)
{
string lang = "unknown";
bool hasLangInfo = line.Contains("(") && line.Contains("): Subtitle: ");
if (hasLangInfo)
lang = line.Split('(')[1].Split(')')[0];
subDict.Add(idx, lang);
idx++;
}
return subDict;
}
public static async Task MergeAudioAndSubs(string inputFile, string audioPath, string tempFolder, int looptimes = -1) // https://superuser.com/a/277667
{
Logger.Log($"[FFCmds] Merging audio from {audioPath} into {inputFile}", true);
string containerExt = Path.GetExtension(inputFile);
string tempPath = Path.Combine(tempFolder, $"vid{containerExt}"); // inputFile + "-temp" + Path.GetExtension(inputFile);
string outPath = Path.Combine(tempFolder, $"muxed{containerExt}"); // inputFile + "-temp" + Path.GetExtension(inputFile);
File.Move(inputFile, tempPath);
string inName = Path.GetFileName(tempPath);
string audioName = Path.GetFileName(audioPath);
string outName = Path.GetFileName(outPath);
bool subs = Utils.ContainerSupportsSubs(containerExt, false) && Config.GetBool("keepSubs");
string subInputArgs = "";
string subMapArgs = "";
string subMetaArgs = "";
string[] subTracks = subs ? IOUtils.GetFilesSorted(tempFolder, false, "*.srt") : new string[0];
for (int subTrack = 0; subTrack < subTracks.Length; subTrack++)
{
subInputArgs += $" -i {Path.GetFileName(subTracks[subTrack])}";
subMapArgs += $" -map {subTrack + 2}";
subMetaArgs += $" -metadata:s:s:{subTrack} language={Path.GetFileNameWithoutExtension(subTracks[subTrack]).Split('-').Last()}";
}
string subCodec = Utils.GetSubCodecForContainer(containerExt);
string args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" +
$"{subInputArgs} -map 0:v -map 1:a {subMapArgs} -c:v copy -c:a copy -c:s {subCodec} {subMetaArgs} -shortest {outName}";
await RunFfmpeg(args, tempFolder, LogMode.Hidden);
if ((File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) || lastOutputFfmpeg.Contains("Invalid data") || lastOutputFfmpeg.Contains("Error initializing output stream"))
{
Logger.Log("Failed to merge audio losslessly! Trying to re-encode.", false, false, "ffmpeg");
args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" +
$"{subInputArgs} -map 0:v -map 1:a {subMapArgs} -c:v copy {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} -c:s {subCodec} {subMetaArgs} -shortest {outName}";
await RunFfmpeg(args, tempFolder, LogMode.Hidden);
if ((File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) || lastOutputFfmpeg.Contains("Invalid data") || lastOutputFfmpeg.Contains("Error initializing output stream"))
{
Logger.Log("Failed to merge audio, even with re-encoding. Output will not have audio.", false, false, "ffmpeg");
IOUtils.TryDeleteIfExists(tempPath);
return;
}
string audioExt = Path.GetExtension(audioPath).Remove(".").ToUpper();
Logger.Log($"Source audio ({audioExt}) has been re-encoded to fit into the target container ({containerExt.Remove(".").ToUpper()}). This may decrease the quality slightly.", false, true, "ffmpeg");
}
if (File.Exists(outPath) && IOUtils.GetFilesize(outPath) > 512)
{
File.Delete(tempPath);
File.Move(outPath, inputFile);
}
else
{
File.Move(tempPath, inputFile);
}
}
}
}

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.Data;
using Flowframes.IO;
using Flowframes.Main;
@@ -11,11 +11,11 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static Flowframes.AvProcess;
using Utils = Flowframes.AudioVideo.FFmpegUtils;
using Utils = Flowframes.Media.FFmpegUtils;
namespace Flowframes
{
class FFmpegCommands
class FfmpegCommands
{
public static string divisionFilter = "\"pad=width=ceil(iw/2)*2:height=ceil(ih/2)*2:color=black@0\"";
public static string pngComprArg = "-compression_level 3";
@@ -31,19 +31,6 @@ namespace Flowframes
await RunFfmpeg(args, concatFile.GetParentDir(), LogMode.Hidden, TaskType.Merge);
}
public static async Task FramesToGifConcat(string framesFile, string outPath, float fps, bool palette, int colors = 64, float resampleFps = -1, LogMode logMode = LogMode.OnlyLastLine)
{
if (logMode != LogMode.Hidden)
Logger.Log((resampleFps <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.ToString().Replace(",", ".")} FPS...");
string vfrFilename = Path.GetFileName(framesFile);
string paletteFilter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither=floyd_steinberg:diff_mode=rectangle\"" : "";
string fpsFilter = (resampleFps <= 0) ? "" : $"fps=fps={resampleFps.ToStringDot()}";
string vf = FormatUtils.ConcatStrings(new string[] { paletteFilter, fpsFilter });
string rate = fps.ToStringDot();
string args = $"-loglevel error -f concat -r {rate} -i {vfrFilename.Wrap()} -f gif {vf} {outPath.Wrap()}";
await RunFfmpeg(args, framesFile.GetParentDir(), LogMode.OnlyLastLine, TaskType.Encode);
}
public static async Task ExtractAlphaDir (string rgbDir, string alphaDir)
{
Directory.CreateDirectory(alphaDir);
@@ -100,142 +87,12 @@ namespace Flowframes
DeleteSource(inputFile);
}
public static async Task ExtractAudio(string inputFile, string outFile) // https://stackoverflow.com/a/27413824/14274419
{
string audioExt = Utils.GetAudioExt(inputFile);
outFile = Path.ChangeExtension(outFile, audioExt);
Logger.Log($"[FFCmds] Extracting audio from {inputFile} to {outFile}", true);
string args = $" -loglevel panic -i {inputFile.Wrap()} -vn -c:a copy {outFile.Wrap()}";
await RunFfmpeg(args, LogMode.Hidden);
if (File.Exists(outFile) && IOUtils.GetFilesize(outFile) < 512)
{
Logger.Log("Failed to extract audio losslessly! Trying to re-encode.");
File.Delete(outFile);
outFile = Path.ChangeExtension(outFile, Utils.GetAudioExtForContainer(Path.GetExtension(inputFile)));
args = $" -loglevel panic -i {inputFile.Wrap()} -vn {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} {outFile.Wrap()}";
await RunFfmpeg(args, LogMode.Hidden);
if ((File.Exists(outFile) && IOUtils.GetFilesize(outFile) < 512) || lastOutputFfmpeg.Contains("Invalid data"))
{
Logger.Log("Failed to extract audio, even with re-encoding. Output will not have audio.");
IOUtils.TryDeleteIfExists(outFile);
return;
}
Logger.Log($"Source audio has been re-encoded as it can't be extracted losslessly. This may decrease the quality slightly.", false, true);
}
}
public static async Task ExtractSubtitles (string inputFile, string outFolder, Interpolate.OutMode outMode)
{
Dictionary<int, string> subtitleTracks = await GetSubtitleTracks(inputFile);
foreach (KeyValuePair<int, string> subTrack in subtitleTracks)
{
string trackName = subTrack.Value.Length > 4 ? CultureInfo.CurrentCulture.TextInfo.ToTitleCase(subTrack.Value.ToLower()) : subTrack.Value.ToUpper();
string outPath = Path.Combine(outFolder, $"{subTrack.Key}-{trackName}.srt");
string args = $" -loglevel error -i {inputFile.Wrap()} -map 0:s:{subTrack.Key} {outPath.Wrap()}";
await RunFfmpeg(args, LogMode.Hidden);
if (lastOutputFfmpeg.Contains("matches no streams")) // Break if there are no more subtitle tracks
break;
Logger.Log($"[FFCmds] Extracted subtitle track {subTrack.Key} to {outPath} ({FormatUtils.Bytes(IOUtils.GetFilesize(outPath))})", true, false, "ffmpeg");
}
if(subtitleTracks.Count > 0)
{
Logger.Log($"Extracted {subtitleTracks.Count} subtitle tracks from the input video.");
Utils.ContainerSupportsSubs(Utils.GetExt(outMode), true);
}
}
public static async Task<Dictionary<int, string>> GetSubtitleTracks (string inputFile)
{
Dictionary<int, string> subDict = new Dictionary<int, string>();
string args = $"-i {inputFile.Wrap()}";
string[] outputLines = (await GetFfmpegOutputAsync(args)).SplitIntoLines();
string[] filteredLines = outputLines.Where(l => l.Contains(" Subtitle: ")).ToArray();
int idx = 0;
foreach(string line in filteredLines)
{
string lang = "unknown";
bool hasLangInfo = line.Contains("(") && line.Contains("): Subtitle: ");
if (hasLangInfo)
lang = line.Split('(')[1].Split(')')[0];
subDict.Add(idx, lang);
idx++;
}
return subDict;
}
public static async Task MergeAudioAndSubs(string inputFile, string audioPath, string tempFolder, int looptimes = -1) // https://superuser.com/a/277667
{
Logger.Log($"[FFCmds] Merging audio from {audioPath} into {inputFile}", true);
string containerExt = Path.GetExtension(inputFile);
string tempPath = Path.Combine(tempFolder, $"vid{containerExt}"); // inputFile + "-temp" + Path.GetExtension(inputFile);
string outPath = Path.Combine(tempFolder, $"muxed{containerExt}"); // inputFile + "-temp" + Path.GetExtension(inputFile);
File.Move(inputFile, tempPath);
string inName = Path.GetFileName(tempPath);
string audioName = Path.GetFileName(audioPath);
string outName = Path.GetFileName(outPath);
bool subs = Utils.ContainerSupportsSubs(containerExt, false) && Config.GetBool("keepSubs");
string subInputArgs = "";
string subMapArgs = "";
string subMetaArgs = "";
string[] subTracks = subs ? IOUtils.GetFilesSorted(tempFolder, false, "*.srt") : new string[0];
for (int subTrack = 0; subTrack < subTracks.Length; subTrack++)
{
subInputArgs += $" -i {Path.GetFileName(subTracks[subTrack])}";
subMapArgs += $" -map {subTrack+2}";
subMetaArgs += $" -metadata:s:s:{subTrack} language={Path.GetFileNameWithoutExtension(subTracks[subTrack]).Split('-').Last()}";
}
string subCodec = Utils.GetSubCodecForContainer(containerExt);
string args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" +
$"{subInputArgs} -map 0:v -map 1:a {subMapArgs} -c:v copy -c:a copy -c:s {subCodec} {subMetaArgs} -shortest {outName}";
await RunFfmpeg(args, tempFolder, LogMode.Hidden);
if ((File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) || lastOutputFfmpeg.Contains("Invalid data") || lastOutputFfmpeg.Contains("Error initializing output stream"))
{
Logger.Log("Failed to merge audio losslessly! Trying to re-encode.", false, false, "ffmpeg");
args = $" -i {inName} -stream_loop {looptimes} -i {audioName.Wrap()}" +
$"{subInputArgs} -map 0:v -map 1:a {subMapArgs} -c:v copy {Utils.GetAudioFallbackArgs(Path.GetExtension(inputFile))} -c:s {subCodec} {subMetaArgs} -shortest {outName}";
await RunFfmpeg(args, tempFolder, LogMode.Hidden);
if ((File.Exists(outPath) && IOUtils.GetFilesize(outPath) < 1024) || lastOutputFfmpeg.Contains("Invalid data") || lastOutputFfmpeg.Contains("Error initializing output stream"))
{
Logger.Log("Failed to merge audio, even with re-encoding. Output will not have audio.", false, false, "ffmpeg");
IOUtils.TryDeleteIfExists(tempPath);
return;
}
string audioExt = Path.GetExtension(audioPath).Remove(".").ToUpper();
Logger.Log($"Source audio ({audioExt}) has been re-encoded to fit into the target container ({containerExt.Remove(".").ToUpper()}). This may decrease the quality slightly.", false, true, "ffmpeg");
}
if(File.Exists(outPath) && IOUtils.GetFilesize(outPath) > 512)
{
File.Delete(tempPath);
File.Move(outPath, inputFile);
}
else
{
File.Move(tempPath, inputFile);
}
}
public static long GetDuration(string inputFile)
{
Logger.Log("Reading Duration using ffprobe.", true, false, "ffprobe");
string args = $" -v panic -select_streams v:0 -show_entries format=duration -of csv=s=x:p=0 -sexagesimal {inputFile.Wrap()}";
string info = GetFfprobeOutput(args);
return FormatUtils.MsFromTimestamp(info);
return -1;
}
public static float GetFramerate(string inputFile)

View File

@@ -10,11 +10,11 @@ using System.IO;
using System.Linq;
using System.Threading.Tasks;
using static Flowframes.AvProcess;
using Utils = Flowframes.AudioVideo.FFmpegUtils;
using Utils = Flowframes.Media.FFmpegUtils;
namespace Flowframes.AudioVideo
namespace Flowframes.Media
{
partial class FfmpegEncode : FFmpegCommands
partial class FfmpegEncode : FfmpegCommands
{
public static async Task FramesToVideoConcat(string framesFile, string outPath, Interpolate.OutMode outMode, float fps, LogMode logMode = LogMode.OnlyLastLine, bool isChunk = false)
{
@@ -36,6 +36,19 @@ namespace Flowframes.AudioVideo
await RunFfmpeg(args, framesFile.GetParentDir(), logMode, TaskType.Encode, !isChunk);
}
public static async Task FramesToGifConcat(string framesFile, string outPath, float fps, bool palette, int colors = 64, float resampleFps = -1, LogMode logMode = LogMode.OnlyLastLine)
{
if (logMode != LogMode.Hidden)
Logger.Log((resampleFps <= 0) ? $"Encoding GIF..." : $"Encoding GIF resampled to {resampleFps.ToString().Replace(",", ".")} FPS...");
string vfrFilename = Path.GetFileName(framesFile);
string paletteFilter = palette ? $"-vf \"split[s0][s1];[s0]palettegen={colors}[p];[s1][p]paletteuse=dither=floyd_steinberg:diff_mode=rectangle\"" : "";
string fpsFilter = (resampleFps <= 0) ? "" : $"fps=fps={resampleFps.ToStringDot()}";
string vf = FormatUtils.ConcatStrings(new string[] { paletteFilter, fpsFilter });
string rate = fps.ToStringDot();
string args = $"-loglevel error -f concat -r {rate} -i {vfrFilename.Wrap()} -f gif {vf} {outPath.Wrap()}";
await RunFfmpeg(args, framesFile.GetParentDir(), LogMode.OnlyLastLine, TaskType.Encode);
}
public static async Task Encode(string inputFile, string vcodec, string acodec, int crf, int audioKbps = 0, bool delSrc = false)
{
string outPath = Path.ChangeExtension(inputFile, null) + "-convert.mp4";

View File

@@ -11,9 +11,9 @@ using System.Linq;
using System.Threading.Tasks;
using static Flowframes.AvProcess;
namespace Flowframes.AudioVideo
namespace Flowframes.Media
{
partial class FfmpegExtract : FFmpegCommands
partial class FfmpegExtract : FfmpegCommands
{
public static async Task ExtractSceneChanges(string inputFile, string frameFolderPath, float rate)
{

View File

@@ -58,6 +58,8 @@ namespace Flowframes.MiscUtils
}
public static long MsFromTimestamp(string timestamp)
{
try
{
string[] values = timestamp.Split(':');
int hours = int.Parse(values[0]);
@@ -67,6 +69,12 @@ namespace Flowframes.MiscUtils
long ms = hours * 3600000 + minutes * 60000 + seconds * 1000 + milliseconds;
return ms;
}
catch (Exception e)
{
Logger.Log("MsFromTimeStamp Exception: " + e.Message);
return 0;
}
}
public static string MsToTimestamp(long milliseconds)
{

View File

@@ -82,7 +82,7 @@ namespace Flowframes
string rgbInterpDir = Path.Combine(Interpolate.current.tempFolder, Paths.interpDir);
string alphaInterpDir = Path.Combine(Interpolate.current.tempFolder, Paths.interpDir + Paths.alphaSuffix);
if (!Directory.Exists(alphaInterpDir)) return;
await FFmpegCommands.MergeAlphaIntoRgb(rgbInterpDir, Padding.interpFrames, alphaInterpDir, Padding.interpFrames, false);
await FfmpegCommands.MergeAlphaIntoRgb(rgbInterpDir, Padding.interpFrames, alphaInterpDir, Padding.interpFrames, false);
}
}

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.IO;
using Flowframes.Magick;
using Flowframes.Main;

View File

@@ -1,4 +1,4 @@
using Flowframes.AudioVideo;
using Flowframes.Media;
using Flowframes.IO;
using Flowframes.Magick;
using Flowframes.Main;
@@ -22,7 +22,7 @@ namespace Flowframes.UI
await FfmpegExtract.VideoToFrames(videoPath, Path.Combine(outPath, Paths.framesDir), false, Interpolate.current.inFps, false, false, false);
File.WriteAllText(Path.Combine(outPath, "fps.ini"), Interpolate.current.inFps.ToString());
if (withAudio)
await FFmpegCommands.ExtractAudio(videoPath, Path.Combine(outPath, "audio"));
await FfmpegAudioAndMetadata.ExtractAudio(videoPath, Path.Combine(outPath, "audio"));
Program.mainForm.SetWorking(false);
Logger.Log("Done.");
Program.mainForm.SetProgress(0);
@@ -34,7 +34,7 @@ namespace Flowframes.UI
return;
int times = loopTimes.GetInt();
Logger.Log("Lopping video " + times + "x...", true);
await FFmpegCommands.LoopVideo(inputFile, times, false);
await FfmpegCommands.LoopVideo(inputFile, times, false);
Logger.Log("Done", true);
Program.mainForm.SetProgress(0);
}
@@ -45,7 +45,7 @@ namespace Flowframes.UI
return;
float speedFloat = speed.GetFloat();
Logger.Log("Creating video with " + speed + "% speed...", true);
await FFmpegCommands.ChangeSpeed(inputFile, speedFloat, false);
await FfmpegCommands.ChangeSpeed(inputFile, speedFloat, false);
Logger.Log("Done", true);
Program.mainForm.SetProgress(0);
}