mirror of
https://github.com/n00mkrad/flowframes.git
synced 2025-12-24 04:09:29 +01:00
FfmpegCommands refactoring, renamed AudioVideo namespace
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using Flowframes.AudioVideo;
|
||||
using Flowframes.Media;
|
||||
using Flowframes.Data;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.Main;
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Flowframes.AudioVideo;
|
||||
using Flowframes.Media;
|
||||
using Flowframes.Data;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.MiscUtils;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Flowframes.AudioVideo;
|
||||
using Flowframes.Media;
|
||||
using Flowframes.Data;
|
||||
using Flowframes.IO;
|
||||
using System;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
148
Code/Media/FfmpegAudioAndMetadata.cs
Normal file
148
Code/Media/FfmpegAudioAndMetadata.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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)
|
||||
@@ -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";
|
||||
@@ -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)
|
||||
{
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Flowframes.AudioVideo;
|
||||
using Flowframes.Media;
|
||||
using Flowframes.IO;
|
||||
using Flowframes.Magick;
|
||||
using Flowframes.Main;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user