mirror of
https://github.com/n00mkrad/flowframes.git
synced 2026-02-24 12:12:36 +01:00
Handle re-encoding (or dropping) or incompatible streams per-stream
This commit is contained in:
@@ -73,7 +73,7 @@ namespace Flowframes.Media
|
||||
|
||||
if (line.MatchesWildcard("*codec*not supported*"))
|
||||
{
|
||||
Interpolate.Cancel($"Error: {line}\n\nTry using a different codec.");
|
||||
Interpolate.Cancel($"Error: {line}\n\nTry using a different codec or container format.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,27 +38,18 @@ namespace Flowframes.Media
|
||||
string inName = Path.GetFileName(tempPath);
|
||||
string outName = Path.GetFileName(outPath);
|
||||
|
||||
string subArgs = "-c:s " + Utils.GetSubCodecForContainer(containerExt);
|
||||
|
||||
bool audioCompat = Utils.ContainerSupportsAllAudioFormats(I.currentSettings.outSettings.Format, GetAudioCodecs(inputVideo));
|
||||
bool slowmo = I.currentSettings.outItsScale != 0 && I.currentSettings.outItsScale != 1;
|
||||
string audioArgs = audioCompat && !slowmo ? "" : await Utils.GetAudioFallbackArgs(inputVideo, I.currentSettings.outSettings.Format, I.currentSettings.outItsScale);
|
||||
|
||||
if (!audioCompat && !slowmo)
|
||||
Logger.Log("Warning: Input audio format(s) not fully supported in output container - Will re-encode.", true, false, "ffmpeg");
|
||||
// if (!audioCompat && !slowmo)
|
||||
// Logger.Log("Warning: Input audio format(s) not fully supported in output container - Will re-encode.", true, false, "ffmpeg");
|
||||
|
||||
bool audio = Config.GetBool(Config.Key.keepAudio);
|
||||
bool subs = Config.GetBool(Config.Key.keepSubs);
|
||||
bool meta = Config.GetBool(Config.Key.keepMeta);
|
||||
|
||||
if (!audio)
|
||||
audioArgs = "-an";
|
||||
|
||||
if (!subs || (subs && !Utils.ContainerSupportsSubs(containerExt)))
|
||||
subArgs = "-sn";
|
||||
string audioArgs = audio ? Utils.MapAudio(I.currentMediaFile, I.currentSettings.outSettings.Format, I.currentSettings.outItsScale) : "-an";
|
||||
string subArgs = subs && Utils.ContainerSupportsSubs(containerExt) ? Utils.MapSubtitles(I.currentMediaFile, I.currentSettings.outSettings.Format) : "-sn";
|
||||
|
||||
bool isMkv = I.currentSettings.outSettings.Format == Data.Enums.Output.Format.Mkv;
|
||||
var muxArgs = new List<string>() { "-map 0:v:0", "-map 1:a:?", "-map 1:s:?", "-c copy", audioArgs, subArgs };
|
||||
var muxArgs = new List<string>() { "-map 0:v:0", "-c:v copy", audioArgs, subArgs };
|
||||
muxArgs.AddIf("-max_interleave_delta 0", isMkv); // https://reddit.com/r/ffmpeg/comments/efddfs/starting_new_cluster_due_to_timestamp/
|
||||
muxArgs.AddIf("-map 1:t?", isMkv && meta); // https://reddit.com/r/ffmpeg/comments/fw4jnh/how_to_make_ffmpeg_keep_attached_images_in_mkv_as/
|
||||
muxArgs.AddIf("-shortest", shortest);
|
||||
|
||||
@@ -412,23 +412,6 @@ namespace Flowframes
|
||||
return compat;
|
||||
}
|
||||
|
||||
public static List<string> GetAudioCodecs(string path, int streamIndex = -1)
|
||||
{
|
||||
Logger.Log($"GetAudioCodecs('{Path.GetFileName(path)}', {streamIndex})", true, false, "ffmpeg");
|
||||
List<string> codecNames = new List<string>();
|
||||
string args = $"-loglevel panic -select_streams a -show_entries stream=codec_name {path.Wrap()}";
|
||||
string info = GetFfprobeOutput(args);
|
||||
string[] entries = info.SplitIntoLines();
|
||||
|
||||
foreach (string entry in entries)
|
||||
{
|
||||
if (entry.Contains("codec_name="))
|
||||
codecNames.Add(entry.Remove("codec_name=").Trim());
|
||||
}
|
||||
|
||||
return codecNames;
|
||||
}
|
||||
|
||||
public static void DeleteSource(string path)
|
||||
{
|
||||
Logger.Log("[FFCmds] Deleting input file/dir: " + path, true);
|
||||
|
||||
@@ -462,6 +462,29 @@ namespace Flowframes.Media
|
||||
return supported;
|
||||
}
|
||||
|
||||
public static bool ContainerSupportsSubtitleCodec(Enums.Output.Format outFormat, string codec)
|
||||
{
|
||||
bool supported = false;
|
||||
|
||||
string[] formatsMp4 = new string[] { "mov_text" };
|
||||
string[] formatsMkv = new string[] { "subrip", "ass", "ssa", "dvdsub", "hdmv_pgs_subtitle", "webvtt", "dvbsub" };
|
||||
string[] formatsWebm = new string[] { "webvtt" };
|
||||
string[] formatsMov = new string[] { "mov_text" };
|
||||
string[] formatsAvi = new string[] { "xsub" };
|
||||
|
||||
switch (outFormat)
|
||||
{
|
||||
case Enums.Output.Format.Mp4: supported = formatsMp4.Contains(codec); break;
|
||||
case Enums.Output.Format.Mkv: supported = formatsMkv.Contains(codec); break;
|
||||
case Enums.Output.Format.Webm: supported = formatsWebm.Contains(codec); break;
|
||||
case Enums.Output.Format.Mov: supported = formatsMov.Contains(codec); break;
|
||||
case Enums.Output.Format.Avi: supported = formatsAvi.Contains(codec); break;
|
||||
}
|
||||
|
||||
Logger.Log($"Checking if {outFormat} supports subtitle format '{codec}': {supported}", true, false, "ffmpeg");
|
||||
return supported;
|
||||
}
|
||||
|
||||
public static string GetExt(OutputSettings settings, bool dot = true)
|
||||
{
|
||||
string ext = dot ? "." : "";
|
||||
@@ -499,44 +522,101 @@ namespace Flowframes.Media
|
||||
return "unsupported";
|
||||
}
|
||||
|
||||
public static async Task<string> GetAudioFallbackArgs(string videoPath, Enums.Output.Format outFormat, float itsScale)
|
||||
public static string GetAudioFallbackArgs(Enums.Output.Format outFormat, int ac, int relIdx)
|
||||
{
|
||||
bool opusMp4 = Config.GetBool(Config.Key.allowOpusInMp4);
|
||||
int opusBr = Config.GetInt(Config.Key.opusBitrate, 128);
|
||||
int aacBr = Config.GetInt(Config.Key.aacBitrate, 160);
|
||||
int ac = (await GetVideoInfo.GetFfprobeInfoAsync(videoPath, GetVideoInfo.FfprobeMode.ShowStreams, "channels", 0)).GetInt();
|
||||
string af = GetAudioFilters(itsScale);
|
||||
|
||||
if (outFormat == Enums.Output.Format.Mkv || outFormat == Enums.Output.Format.Webm || (outFormat == Enums.Output.Format.Mp4 && opusMp4))
|
||||
return $"-c:a libopus -b:a {(ac > 4 ? $"{opusBr * 2}" : $"{opusBr}")}k -ac {(ac > 0 ? $"{ac}" : "2")} {af}"; // Double bitrate if 5ch or more, ignore ac if <= 0
|
||||
if (outFormat == Enums.Output.Format.Mkv || outFormat == Enums.Output.Format.Webm || (outFormat == Enums.Output.Format.Mp4))
|
||||
return $"-c:a:{relIdx} libopus -b:a:{relIdx} {(ac > 4 ? $"{opusBr * 2}" : $"{opusBr}")}k -ac:a:{relIdx} {(ac > 0 ? $"{ac}" : "2")}"; // Double bitrate if 5ch or more, ignore ac if <= 0
|
||||
else
|
||||
return $"-c:a aac -b:a {(ac > 4 ? $"{aacBr * 2}" : $"{aacBr}")}k -aac_coder twoloop -ac {(ac > 0 ? $"{ac}" : "2")} {af}";
|
||||
return $"-c:a:{relIdx} aac -b:a:{relIdx} {(ac > 4 ? $"{aacBr * 2}" : $"{aacBr}")}k -aac_coder twoloop -ac:a:{relIdx} {(ac > 0 ? $"{ac}" : "2")}";
|
||||
}
|
||||
|
||||
public static string MapAudio(MediaFile m, Enums.Output.Format outFormat, float itsScale)
|
||||
{
|
||||
string filters = GetAudioFilters(itsScale);
|
||||
Dictionary<string, bool> supported = new Dictionary<string, bool>();
|
||||
|
||||
foreach (var a in m.AudioStreams)
|
||||
{
|
||||
supported[a.Codec] = filters.IsEmpty() && ContainerSupportsAudioFormat(outFormat, a.Codec); // Filters = Re-encoding required
|
||||
}
|
||||
|
||||
// If all are supported, simply copy all streams
|
||||
if (supported.All(x => x.Value))
|
||||
return "-map 1:a -c:a copy";
|
||||
|
||||
// Otherwise, map each stream individually
|
||||
string args = "";
|
||||
int relIdx = 0;
|
||||
foreach (var a in m.AudioStreams)
|
||||
{
|
||||
if (supported[a.Codec])
|
||||
args += $"-map 1:{a.Index} -c:a:{relIdx} copy ";
|
||||
else
|
||||
args += $"-map 1:{a.Index} {GetAudioFallbackArgs(outFormat, a.Channels, relIdx)} ";
|
||||
relIdx++;
|
||||
}
|
||||
|
||||
return args.TrimEnd();
|
||||
}
|
||||
|
||||
private static string GetAudioFilters(float itsScale)
|
||||
{
|
||||
if (itsScale == 0 || itsScale == 1)
|
||||
if (itsScale <= 0.0001 || itsScale == 1)
|
||||
return "";
|
||||
|
||||
if (itsScale > 4)
|
||||
return $"-af atempo=0.5,atempo=0.5,atempo={((1f / itsScale) * 4).ToString()}";
|
||||
else if (itsScale > 2)
|
||||
return $"-af atempo=0.5,atempo={((1f / itsScale) * 2).ToString()}";
|
||||
else
|
||||
return $"-af atempo={(1f / itsScale).ToString()}";
|
||||
return $"-af atempo=0.5,atempo=0.5,atempo={((1f / itsScale) * 4)}";
|
||||
if (itsScale > 2)
|
||||
return $"-af atempo=0.5,atempo={((1f / itsScale) * 2)}";
|
||||
return $"-af atempo={(1f / itsScale)}";
|
||||
}
|
||||
|
||||
public static string GetSubCodecForContainer(string containerExt)
|
||||
public static string MapSubtitles(MediaFile m, Enums.Output.Format outFormat)
|
||||
{
|
||||
containerExt = containerExt.Remove(".");
|
||||
Dictionary<string, string> codec = new Dictionary<string, string>();
|
||||
|
||||
if (containerExt == "mp4" || containerExt == "mov")
|
||||
foreach (var a in m.SubtitleStreams)
|
||||
{
|
||||
codec[a.Codec] = GetSubCodecForContainer(outFormat, a.Codec);
|
||||
}
|
||||
|
||||
// If all are supported, simply copy all streams
|
||||
if (codec.All(x => x.Value == "copy"))
|
||||
return "-map 1:s -c:s copy";
|
||||
|
||||
// Otherwise, map each stream individually
|
||||
string args = "";
|
||||
int relIdx = 0;
|
||||
foreach (var a in m.SubtitleStreams)
|
||||
{
|
||||
args += codec[a.Codec].IsEmpty() ? "" : $"-map 1:{a.Index} -c:s:{relIdx} {codec[a.Codec]} ";
|
||||
relIdx++;
|
||||
}
|
||||
|
||||
return args.TrimEnd();
|
||||
}
|
||||
|
||||
public static string GetSubCodecForContainer(Enums.Output.Format outFormat, string codec)
|
||||
{
|
||||
bool muxSupport = ContainerSupportsSubtitleCodec(outFormat, codec);
|
||||
|
||||
if (muxSupport)
|
||||
return "copy";
|
||||
|
||||
var textCodecs = new List<string> { "text", "ssa", "mov_text", "srt", "subrip", "webvtt", "ass", "hdmv_text_subtitle", "ttml" };
|
||||
|
||||
if(!textCodecs.Contains(codec))
|
||||
return ""; // -> Won't be included
|
||||
|
||||
if (outFormat == Enums.Output.Format.Mp4 || outFormat == Enums.Output.Format.Mov)
|
||||
return "mov_text";
|
||||
|
||||
if (containerExt == "webm")
|
||||
if (outFormat == Enums.Output.Format.Webm)
|
||||
return "webvtt";
|
||||
|
||||
return "copy"; // Default: Copy subs
|
||||
return ""; // -> Won't be included
|
||||
}
|
||||
|
||||
public static bool ContainerSupportsSubs(string containerExt, bool showWarningIfNotSupported = true)
|
||||
|
||||
Reference in New Issue
Block a user