mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-01-04 21:40:48 -06:00
VideoNodes: changed VideoEncode to take in List<string> instead of string. Updated Audio Normalize to use VideoEncode for 2 pass 1st pass
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
namespace FileFlows.VideoNodes
|
||||
{
|
||||
using System.Linq;
|
||||
internal static class ExtensionMethods
|
||||
{
|
||||
public static void AddOrUpdate(this Dictionary<string, object> dict, string key, object value) {
|
||||
@@ -12,5 +13,44 @@
|
||||
{
|
||||
return str == string.Empty ? null : str;
|
||||
}
|
||||
|
||||
public static IEnumerable<string> SplitCommandLine(this string commandLine)
|
||||
{
|
||||
bool inQuotes = false;
|
||||
|
||||
return commandLine.Split(c =>
|
||||
{
|
||||
if (c == '\"')
|
||||
inQuotes = !inQuotes;
|
||||
|
||||
return !inQuotes && c == ' ';
|
||||
})
|
||||
.Select(arg => arg.Trim().TrimMatchingQuotes('\"'))
|
||||
.Where(arg => !string.IsNullOrEmpty(arg));
|
||||
}
|
||||
public static IEnumerable<string> Split(this string str,
|
||||
Func<char, bool> controller)
|
||||
{
|
||||
int nextPiece = 0;
|
||||
|
||||
for (int c = 0; c < str.Length; c++)
|
||||
{
|
||||
if (controller(str[c]))
|
||||
{
|
||||
yield return str.Substring(nextPiece, c - nextPiece);
|
||||
nextPiece = c + 1;
|
||||
}
|
||||
}
|
||||
|
||||
yield return str.Substring(nextPiece);
|
||||
}
|
||||
public static string TrimMatchingQuotes(this string input, char quote)
|
||||
{
|
||||
if ((input.Length >= 2) &&
|
||||
(input[0] == quote) && (input[input.Length - 1] == quote))
|
||||
return input.Substring(1, input.Length - 2);
|
||||
|
||||
return input;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,22 +26,31 @@ namespace FileFlows.VideoNodes
|
||||
this.Logger = logger;
|
||||
}
|
||||
|
||||
public bool Encode(string input, string output, string arguments, bool dontAddInputFile = false)
|
||||
public (bool successs, string output) Encode(string input, string output, List<string> arguments, bool dontAddInputFile = false)
|
||||
{
|
||||
// -y means it will overwrite a file if output already exists
|
||||
if(dontAddInputFile == false)
|
||||
arguments = $"-i \"{input}\" -y {arguments} \"{output}\"";
|
||||
else
|
||||
arguments = $"{arguments} \"{output}\"";
|
||||
arguments ??= new List<string> ();
|
||||
|
||||
Logger.ILog(new string('=', ("FFMpeg.Arguments: " + arguments).Length));
|
||||
Logger.ILog("FFMpeg.Arguments: " + arguments);
|
||||
Logger.ILog(new string('=', ("FFMpeg.Arguments: " + arguments).Length));
|
||||
// -y means it will overwrite a file if output already exists
|
||||
if (dontAddInputFile == false) {
|
||||
arguments.Insert(0, "-i");
|
||||
arguments.Insert(1, input);
|
||||
arguments.Insert(2, "-y");
|
||||
}
|
||||
|
||||
if (arguments.Last() != "-")
|
||||
arguments.Add(output);
|
||||
else
|
||||
Logger.ILog("Last argument '-' skipping adding output file");
|
||||
|
||||
string argsString = String.Join(" ", arguments.Select(x => x.IndexOf(" ") > 0 ? "\"" + x + "\"" : x));
|
||||
Logger.ILog(new string('=', ("FFMpeg.Arguments: " + argsString).Length));
|
||||
Logger.ILog("FFMpeg.Arguments: " + argsString);
|
||||
Logger.ILog(new string('=', ("FFMpeg.Arguments: " + argsString).Length));
|
||||
|
||||
var task = ExecuteShellCommand(ffMpegExe, arguments, 0);
|
||||
task.Wait();
|
||||
Logger.ILog("Exit Code: " + task.Result.ExitCode);
|
||||
return task.Result.ExitCode == 0; // exitcode 0 means it was successful
|
||||
return (task.Result.ExitCode == 0, task.Result.Output); // exitcode 0 means it was successful
|
||||
}
|
||||
|
||||
internal void Cancel()
|
||||
@@ -58,7 +67,7 @@ namespace FileFlows.VideoNodes
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
public async Task<ProcessResult> ExecuteShellCommand(string command, string arguments, int timeout = 0)
|
||||
public async Task<ProcessResult> ExecuteShellCommand(string command, List<string> arguments, int timeout = 0)
|
||||
{
|
||||
var result = new ProcessResult();
|
||||
|
||||
@@ -69,7 +78,11 @@ namespace FileFlows.VideoNodes
|
||||
// To fix it you can try to add '#!/bin/bash' header to the script.
|
||||
|
||||
process.StartInfo.FileName = command;
|
||||
process.StartInfo.Arguments = arguments;
|
||||
if (arguments?.Any() == true)
|
||||
{
|
||||
foreach (string arg in arguments)
|
||||
process.StartInfo.ArgumentList.Add(arg); ;
|
||||
}
|
||||
process.StartInfo.UseShellExecute = false;
|
||||
process.StartInfo.RedirectStandardInput = true;
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
@@ -125,12 +138,7 @@ namespace FileFlows.VideoNodes
|
||||
{
|
||||
result.Completed = true;
|
||||
result.ExitCode = process.ExitCode;
|
||||
|
||||
// Adds process output if it was completed with error
|
||||
if (process.ExitCode != 0)
|
||||
{
|
||||
result.Output = $"{outputBuilder}{errorBuilder}";
|
||||
}
|
||||
result.Output = $"{outputBuilder}{errorBuilder}";
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -30,7 +30,7 @@ namespace VideoNodes.Tests
|
||||
|
||||
new VideoFile().Execute(args);
|
||||
|
||||
string output = AudioNormalization.DoTwoPass(args, ffmpeg, "0:1");
|
||||
string output = node.DoTwoPass(args, ffmpeg, "0:1");
|
||||
Assert.IsFalse(string.IsNullOrWhiteSpace(output));
|
||||
}
|
||||
|
||||
|
||||
@@ -43,25 +43,27 @@
|
||||
return 2;
|
||||
}
|
||||
|
||||
List<string> ffArgs = new List<string>();
|
||||
ffArgs.Add($"-map 0:v");
|
||||
List<string> ffArgs = new List<string>
|
||||
{
|
||||
"-c", "copy",
|
||||
"-map", "0:v",
|
||||
};
|
||||
|
||||
float volume = this.VolumePercent / 100f;
|
||||
foreach (var audio in videoInfo.AudioStreams)
|
||||
{
|
||||
ffArgs.Add($"-map 0:{audio.Index} -filter:a \"volume={volume.ToString(".0######")}\"");
|
||||
ffArgs.AddRange(new[] { "-map", $"0:{audio.Index}", "-filter:a", $"volume={volume.ToString(".0######")}" });
|
||||
}
|
||||
|
||||
if (videoInfo.SubtitleStreams?.Any() == true)
|
||||
ffArgs.Add("-map 0:s -c copy");
|
||||
ffArgs.AddRange(new[] { "-map", "0:s" });
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
|
||||
string extension = new FileInfo(args.WorkingFile).Extension;
|
||||
if(extension.StartsWith("."))
|
||||
extension = extension.Substring(1);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine, extension) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -44,10 +44,10 @@
|
||||
|
||||
List<string> ffArgs = new List<string>();
|
||||
|
||||
ffArgs.Add("-c copy");
|
||||
ffArgs.AddRange(new[] { "-c", "copy" });
|
||||
|
||||
if(videoInfo.VideoStreams?.Any() == true)
|
||||
ffArgs.Add($"-map 0:v");
|
||||
if (videoInfo.VideoStreams?.Any() == true)
|
||||
ffArgs.AddRange(new[] { "-map", "0:v" });
|
||||
|
||||
|
||||
for (int j = 0; j < videoInfo.AudioStreams.Count;j++)
|
||||
@@ -59,26 +59,24 @@
|
||||
if (TwoPass)
|
||||
{
|
||||
string twoPass = DoTwoPass(args, ffmpegExe, audio.IndexString);
|
||||
ffArgs.Add($"-map 0:{audio.Index} -c:a {audio.Codec} -ar {sampleRate} {twoPass}");
|
||||
ffArgs.AddRange(new[] { "-map", $"0:{audio.Index}", "-c:a", audio.Codec, "-ar", sampleRate.ToString(), "-af", twoPass });
|
||||
}
|
||||
else
|
||||
{
|
||||
ffArgs.Add($"-map 0:{audio.Index} -c:a {audio.Codec} -ar {sampleRate} -af loudnorm={LOUDNORM_TARGET}");
|
||||
ffArgs.AddRange(new[] { "-map", $"0:{audio.Index}", "-c:a", audio.Codec, "-ar", sampleRate.ToString(), "-af", $"loudnorm={LOUDNORM_TARGET}" });
|
||||
}
|
||||
}
|
||||
else
|
||||
ffArgs.Add($"-map 0:{audio.Index}");
|
||||
}
|
||||
if (videoInfo.SubtitleStreams?.Any() == true)
|
||||
ffArgs.Add("-map 0:s");
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
ffArgs.AddRange(new[] { "-map", "0:s" });
|
||||
|
||||
string extension = new FileInfo(args.WorkingFile).Extension;
|
||||
if (extension.StartsWith("."))
|
||||
extension = extension.Substring(1);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine, extension) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
@@ -91,10 +89,11 @@
|
||||
}
|
||||
|
||||
[RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize<FileFlows.VideoNodes.AudioNormalization.LoudNormStats>(string, System.Text.Json.JsonSerializerOptions?)")]
|
||||
public static string DoTwoPass(NodeParameters args,string ffmpegExe, string audioIndex)
|
||||
public string DoTwoPass(NodeParameters args,string ffmpegExe, string audioIndex)
|
||||
{
|
||||
//-af loudnorm=I=-24:LRA=7:TP=-2.0"
|
||||
var result = args.Execute(ffmpegExe, argumentList: new[]
|
||||
string output;
|
||||
var result = Encode(args, ffmpegExe, new List<string>
|
||||
{
|
||||
"-hide_banner",
|
||||
"-i", args.WorkingFile,
|
||||
@@ -102,20 +101,20 @@
|
||||
"-af", "loudnorm=" + LOUDNORM_TARGET + ":print_format=json",
|
||||
"-f", "null",
|
||||
"-"
|
||||
});
|
||||
}, out output, updateWorkingFile: false, dontAddInputFile: true);
|
||||
|
||||
if (result.ExitCode != 0)
|
||||
if (result == false)
|
||||
throw new Exception("Failed to prcoess audio track");
|
||||
|
||||
int index = result.Output.LastIndexOf("{");
|
||||
int index = output.LastIndexOf("{");
|
||||
if (index == -1)
|
||||
throw new Exception("Failed to detected json in output");
|
||||
string json = result.Output.Substring(index);
|
||||
string json = output.Substring(index);
|
||||
json = json.Substring(0, json.IndexOf("}") + 1);
|
||||
if (string.IsNullOrEmpty(json))
|
||||
throw new Exception("Failed to parse TwoPass json");
|
||||
LoudNormStats stats = JsonSerializer.Deserialize<LoudNormStats>(json);
|
||||
string ar = $"-af loudnorm=print_format=summary:linear=true:{LOUDNORM_TARGET}:measured_I={stats.input_i}:measured_LRA={stats.input_lra}:measured_tp={stats.input_tp}:measured_thresh={stats.input_thresh}:offset={stats.target_offset}";
|
||||
string ar = $"loudnorm=print_format=summary:linear=true:{LOUDNORM_TARGET}:measured_I={stats.input_i}:measured_LRA={stats.input_lra}:measured_tp={stats.input_tp}:measured_thresh={stats.input_thresh}:offset={stats.target_offset}";
|
||||
return ar;
|
||||
}
|
||||
|
||||
|
||||
@@ -98,8 +98,12 @@
|
||||
if (string.IsNullOrEmpty(ffmpegExe))
|
||||
return -1;
|
||||
|
||||
List<string> ffArgs = new List<string>();
|
||||
ffArgs.Add($"-map 0:v");
|
||||
List<string> ffArgs = new List<string>
|
||||
{
|
||||
"-c", "copy",
|
||||
"-map", "0:v",
|
||||
};
|
||||
|
||||
|
||||
OrderedTracks = OrderedTracks?.Select(x => x.ToLower())?.ToList() ?? new ();
|
||||
|
||||
@@ -115,22 +119,20 @@
|
||||
|
||||
foreach (var audio in reordered)
|
||||
{
|
||||
ffArgs.Add($"-map " + audio.IndexString);
|
||||
ffArgs.AddRange(new[] { "-map", audio.IndexString });
|
||||
}
|
||||
|
||||
if (videoInfo.SubtitleStreams?.Any() == true)
|
||||
ffArgs.Add("-map 0:s -c copy");
|
||||
ffArgs.AddRange(new[] { "-map", "0:s" });
|
||||
|
||||
// this makes the first audio track now the default track
|
||||
ffArgs.Add("-disposition:a:0 default");
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
ffArgs.AddRange(new[] { "-disposition:a:0", "default" });
|
||||
|
||||
string extension = new FileInfo(args.WorkingFile).Extension;
|
||||
if(extension.StartsWith("."))
|
||||
extension = extension.Substring(1);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine, extension) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
{
|
||||
if (string.IsNullOrEmpty(at.Language))
|
||||
{
|
||||
ffArgs.Add($"-metadata:s:a:{index} language={Language.ToLower()}");
|
||||
ffArgs.AddRange(new[] { $"-metadata:s:a:{index}", $"language={Language.ToLower()}" });
|
||||
}
|
||||
++index;
|
||||
}
|
||||
@@ -43,9 +43,10 @@
|
||||
return 2; // nothing to do
|
||||
|
||||
|
||||
ffArgs.Insert(0, $"-map 0 -c copy");
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
ffArgs.Insert(0, "-map");
|
||||
ffArgs.Insert(1, "0");
|
||||
ffArgs.Insert(2, "-c");
|
||||
ffArgs.Insert(3, "copy");
|
||||
|
||||
string extension = new FileInfo(args.WorkingFile).Extension;
|
||||
if(extension.StartsWith("."))
|
||||
@@ -53,7 +54,7 @@
|
||||
args.Logger?.DLog("Working file: " + args.WorkingFile);
|
||||
args.Logger?.DLog("Extension: " + extension);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine, extension) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -69,8 +69,8 @@
|
||||
string tempMetaDataFile = Path.Combine(args.TempPath, Guid.NewGuid().ToString() + ".txt");
|
||||
File.WriteAllText(tempMetaDataFile, metadata.ToString());
|
||||
|
||||
string ffArgs = $"-i \"{tempMetaDataFile}\" -map_metadata 1 -codec copy -max_muxing_queue_size 1024";
|
||||
if (Encode(args, ffmpegExe, ffArgs))
|
||||
string[] ffArgs = new[] { "-i", tempMetaDataFile, "-map_metadata", "1", "-codec", "copy", "-max_muxing_queue_size", "1024" };
|
||||
if (Encode(args, ffmpegExe, ffArgs.ToList()))
|
||||
{
|
||||
args.Logger?.ILog($"Adding {chapter} chapters to file");
|
||||
return 1;
|
||||
|
||||
@@ -89,7 +89,15 @@
|
||||
string concatList = segmentPrefix + "concatlist.txt";
|
||||
File.WriteAllLines(concatList, segments.Select(x => $"file '{x}'"));
|
||||
|
||||
bool concatResult = Encode(args, ffmpegExe, $"-f concat -safe 0 -i \"{concatList}\" -c copy", dontAddInputFile: true, extension: extension);
|
||||
List<string> ffArgs = new List<string>
|
||||
{
|
||||
"-f", "concat",
|
||||
"-safe", "0",
|
||||
"-i", "concatList",
|
||||
"-c", "copy"
|
||||
};
|
||||
|
||||
bool concatResult = Encode(args, ffmpegExe, ffArgs, dontAddInputFile: true, extension: extension);
|
||||
|
||||
foreach(string segment in segments.Union(new[] { concatList }))
|
||||
{
|
||||
@@ -108,7 +116,13 @@
|
||||
bool EncodeSegment(float start, float duration)
|
||||
{
|
||||
string segment = segmentPrefix + (++count).ToString("D2") + "." + extension;
|
||||
if (Encode(args, ffmpegExe, $"-ss {start} -t {duration} -c copy", outputFile: segment, updateWorkingFile: false))
|
||||
List<string> ffArgs = new List<string>
|
||||
{
|
||||
"-ss", start.ToString(),
|
||||
"-t", duration.ToString(),
|
||||
"-c", "copy"
|
||||
};
|
||||
if (Encode(args, ffmpegExe, ffArgs, outputFile: segment, updateWorkingFile: false))
|
||||
{
|
||||
segments.Add(segment);
|
||||
return true;
|
||||
|
||||
@@ -18,7 +18,13 @@ namespace FileFlows.VideoNodes
|
||||
|
||||
private FFMpegEncoder Encoder;
|
||||
|
||||
protected bool Encode(NodeParameters args, string ffmpegExe, string ffmpegParameters, string extension = "mkv", string outputFile = "", bool updateWorkingFile = true, bool dontAddInputFile = false)
|
||||
protected bool Encode(NodeParameters args, string ffmpegExe, List<string> ffmpegParameters, string extension = "mkv", string outputFile = "", bool updateWorkingFile = true, bool dontAddInputFile = false)
|
||||
{
|
||||
string output;
|
||||
return Encode(args, ffmpegExe, ffmpegParameters, out output, extension, outputFile, updateWorkingFile, dontAddInputFile);
|
||||
}
|
||||
|
||||
protected bool Encode(NodeParameters args, string ffmpegExe, List<string> ffmpegParameters, out string ouput, string extension = "mkv", string outputFile = "", bool updateWorkingFile = true, bool dontAddInputFile = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(extension))
|
||||
extension = "mkv";
|
||||
@@ -40,9 +46,9 @@ namespace FileFlows.VideoNodes
|
||||
}
|
||||
}
|
||||
|
||||
bool success = Encoder.Encode(args.WorkingFile, outputFile, ffmpegParameters, dontAddInputFile: dontAddInputFile);
|
||||
args.Logger.ILog("Encoding successful: " + success);
|
||||
if (success && updateWorkingFile)
|
||||
var success = Encoder.Encode(args.WorkingFile, outputFile, ffmpegParameters, dontAddInputFile: dontAddInputFile);
|
||||
args.Logger.ILog("Encoding successful: " + success.successs);
|
||||
if (success.successs && updateWorkingFile)
|
||||
{
|
||||
args.SetWorkingFile(outputFile);
|
||||
|
||||
@@ -54,7 +60,8 @@ namespace FileFlows.VideoNodes
|
||||
}
|
||||
Encoder.AtTime -= AtTimeEvent;
|
||||
Encoder = null;
|
||||
return success;
|
||||
ouput = success.output;
|
||||
return success.successs;
|
||||
}
|
||||
|
||||
public override Task Cancel()
|
||||
|
||||
@@ -44,11 +44,14 @@ namespace FileFlows.VideoNodes
|
||||
|
||||
string outputFile = Path.Combine(args.TempPath, Guid.NewGuid().ToString() + "." + Extension);
|
||||
|
||||
string cmd = CommandLine.Replace("{WorkingFile}", "\"" + args.WorkingFile + "\"")
|
||||
.Replace("{Output}", outputFile)
|
||||
.Replace("{output}", outputFile);
|
||||
List<string> ffArgs = CommandLine.SplitCommandLine().Select(x =>
|
||||
{
|
||||
if (x.ToLower() == "{workingfile}") return args.WorkingFile;
|
||||
if (x.ToLower() == "{output}") return outputFile;
|
||||
return x;
|
||||
}).ToList();
|
||||
|
||||
if (Encode(args, ffmpegExe, cmd) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
|
||||
try
|
||||
{
|
||||
if (Encode(args, ffmpegExe, "-c copy -map 0", "mkv") == false)
|
||||
if (Encode(args, ffmpegExe, new List<string> { "-c", "copy", "-map", "0" }, "mkv") == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
@@ -54,7 +54,7 @@
|
||||
|
||||
try
|
||||
{
|
||||
if (Encode(args, ffmpegExe, "-c copy -map 0", "mp4") == false)
|
||||
if (Encode(args, ffmpegExe, new List<string> { "-c", "copy", "-map", "0" }, "mp4") == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -55,8 +55,11 @@
|
||||
if (string.IsNullOrEmpty(ffmpegExe))
|
||||
return -1;
|
||||
|
||||
List<string> ffArgs = new List<string>();
|
||||
ffArgs.Add($"-map 0:v -map 0:a");
|
||||
List<string> ffArgs = new List<string>()
|
||||
{
|
||||
"-map", "0:v",
|
||||
"-map", "0:a",
|
||||
};
|
||||
|
||||
var removeCodecs = SubtitlesToRemove?.Where(x => string.IsNullOrWhiteSpace(x) == false)?.Select(x => x.ToLower())?.ToList() ?? new List<string>();
|
||||
|
||||
@@ -73,7 +76,7 @@
|
||||
foundBadSubtitle = true;
|
||||
continue;
|
||||
}
|
||||
ffArgs.Add("-map " + sub.IndexString);
|
||||
ffArgs.AddRange(new[] { "-map", sub.IndexString});
|
||||
}
|
||||
|
||||
if(foundBadSubtitle == false)
|
||||
@@ -81,15 +84,14 @@
|
||||
// nothing to remove
|
||||
return 2;
|
||||
}
|
||||
ffArgs.Add("-c copy");
|
||||
ffArgs.AddRange(new[] { "-c", "copy" });
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
|
||||
string extension = new FileInfo(args.WorkingFile).Extension;
|
||||
if(extension.StartsWith("."))
|
||||
extension = extension.Substring(1);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine, extension) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs, extension) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -140,8 +140,8 @@ namespace FileFlows.VideoNodes
|
||||
|
||||
List<string> ffArgs = new List<string>();
|
||||
|
||||
ffArgs.Add(encodeVideoParameters?.EmptyAsNull() ?? copyVideoStream);
|
||||
ffArgs.Add(encodeAudioParameters?.EmptyAsNull() ?? copyAudioStream);
|
||||
ffArgs.AddRange((encodeVideoParameters?.EmptyAsNull() ?? copyVideoStream).Split(" "));
|
||||
ffArgs.AddRange((encodeAudioParameters?.EmptyAsNull() ?? copyAudioStream).Split(" "));
|
||||
|
||||
TotalTime = videoInfo.VideoStreams[0].Duration;
|
||||
args.Logger.ILog("### Total Time: " + TotalTime);
|
||||
@@ -151,9 +151,9 @@ namespace FileFlows.VideoNodes
|
||||
if (SupportsSubtitles(args, videoInfo, Extension))
|
||||
{
|
||||
if (Language != string.Empty)
|
||||
ffArgs.Add($"-map 0:s:m:language:{Language}? -c:s copy");
|
||||
ffArgs.AddRange(new[] { "-map", $"0:s:m:language:{Language}?", "-c:s", "copy" });
|
||||
else
|
||||
ffArgs.Add($"-map 0:s? -c:s copy");
|
||||
ffArgs.AddRange(new[] { "-map", "0:s?", "-c:s", "copy" });
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -161,9 +161,8 @@ namespace FileFlows.VideoNodes
|
||||
}
|
||||
}
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine, Extension) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs, Extension) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -88,14 +88,15 @@ namespace FileFlows.VideoNodes
|
||||
if (string.IsNullOrEmpty(ffmpegExe))
|
||||
return -1;
|
||||
|
||||
List<string> ffArgs = new List<string>();
|
||||
|
||||
string codec = CheckVideoCodec(ffmpegExe, VideoCodec);
|
||||
ffArgs.Add($"-vf scale={Resolution}:flags=lanczos -c:v {codec}");
|
||||
List<string> ffArgs = new List<string>()
|
||||
{
|
||||
"-vf", $"scale={Resolution}:flags=lanczos",
|
||||
"-c:v", "codec"
|
||||
};
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine, Extension) == false)
|
||||
if (Encode(args, ffmpegExe, ffArgs, Extension) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
|
||||
@@ -1,140 +1,140 @@
|
||||
namespace FileFlows.VideoNodes
|
||||
{
|
||||
using System.ComponentModel;
|
||||
using System.Text.RegularExpressions;
|
||||
using FileFlows.Plugin;
|
||||
using FileFlows.Plugin.Attributes;
|
||||
//namespace FileFlows.VideoNodes
|
||||
//{
|
||||
// using System.ComponentModel;
|
||||
// using System.Text.RegularExpressions;
|
||||
// using FileFlows.Plugin;
|
||||
// using FileFlows.Plugin.Attributes;
|
||||
|
||||
public class Video_H265_AC3 : EncodingNode
|
||||
{
|
||||
// public class Video_H265_AC3 : EncodingNode
|
||||
// {
|
||||
|
||||
[DefaultValue("eng")]
|
||||
[Text(1)]
|
||||
public string Language { get; set; }
|
||||
// [DefaultValue("eng")]
|
||||
// [Text(1)]
|
||||
// public string Language { get; set; }
|
||||
|
||||
[DefaultValue(21)]
|
||||
[NumberInt(2)]
|
||||
public int Crf { get; set; }
|
||||
[DefaultValue(true)]
|
||||
[Boolean(3)]
|
||||
public bool NvidiaEncoding { get; set; }
|
||||
[DefaultValue(0)]
|
||||
[NumberInt(4)]
|
||||
public int Threads { get; set; }
|
||||
// [DefaultValue(21)]
|
||||
// [NumberInt(2)]
|
||||
// public int Crf { get; set; }
|
||||
// [DefaultValue(true)]
|
||||
// [Boolean(3)]
|
||||
// public bool NvidiaEncoding { get; set; }
|
||||
// [DefaultValue(0)]
|
||||
// [NumberInt(4)]
|
||||
// public int Threads { get; set; }
|
||||
|
||||
[DefaultValue(false)]
|
||||
[Boolean(5)]
|
||||
public bool NormalizeAudio { get; set; }
|
||||
// [DefaultValue(false)]
|
||||
// [Boolean(5)]
|
||||
// public bool NormalizeAudio { get; set; }
|
||||
|
||||
|
||||
[DefaultValue(false)]
|
||||
[Boolean(6)]
|
||||
public bool ForceRencode { get; set; }
|
||||
// [DefaultValue(false)]
|
||||
// [Boolean(6)]
|
||||
// public bool ForceRencode { get; set; }
|
||||
|
||||
|
||||
public override string Icon => "far fa-file-video";
|
||||
// public override string Icon => "far fa-file-video";
|
||||
|
||||
public override int Execute(NodeParameters args)
|
||||
{
|
||||
this.args = args;
|
||||
try
|
||||
{
|
||||
VideoInfo videoInfo = GetVideoInfo(args);
|
||||
if (videoInfo == null)
|
||||
return -1;
|
||||
// public override int Execute(NodeParameters args)
|
||||
// {
|
||||
// this.args = args;
|
||||
// try
|
||||
// {
|
||||
// VideoInfo videoInfo = GetVideoInfo(args);
|
||||
// if (videoInfo == null)
|
||||
// return -1;
|
||||
|
||||
Language = Language?.ToLower() ?? "";
|
||||
// Language = Language?.ToLower() ?? "";
|
||||
|
||||
// ffmpeg is one based for stream index, so video should be 1, audio should be 2
|
||||
// // ffmpeg is one based for stream index, so video should be 1, audio should be 2
|
||||
|
||||
var videoH265 = videoInfo.VideoStreams.FirstOrDefault(x => Regex.IsMatch(x.Codec ?? "", @"^(hevc|h(\.)?265)$", RegexOptions.IgnoreCase));
|
||||
var videoTrack = videoH265 ?? videoInfo.VideoStreams[0];
|
||||
args.Logger.ILog("Video: ", videoTrack);
|
||||
// var videoH265 = videoInfo.VideoStreams.FirstOrDefault(x => Regex.IsMatch(x.Codec ?? "", @"^(hevc|h(\.)?265)$", RegexOptions.IgnoreCase));
|
||||
// var videoTrack = videoH265 ?? videoInfo.VideoStreams[0];
|
||||
// args.Logger.ILog("Video: ", videoTrack);
|
||||
|
||||
var bestAudio = videoInfo.AudioStreams.Where(x => System.Text.Json.JsonSerializer.Serialize(x).ToLower().Contains("commentary") == false)
|
||||
.OrderBy(x =>
|
||||
{
|
||||
if (Language != string.Empty)
|
||||
{
|
||||
args.Logger.ILog("Language: " + x.Language, x);
|
||||
if (string.IsNullOrEmpty(x.Language))
|
||||
return 50; // no language specified
|
||||
if (x.Language?.ToLower() != Language)
|
||||
return 100; // low priority not the desired language
|
||||
}
|
||||
return 0;
|
||||
})
|
||||
.ThenByDescending(x => x.Channels)
|
||||
//.ThenBy(x => x.CodecName.ToLower() == "ac3" ? 0 : 1) // if we do this we can get commentary tracks...
|
||||
.ThenBy(x => x.Index)
|
||||
.FirstOrDefault();
|
||||
// var bestAudio = videoInfo.AudioStreams.Where(x => System.Text.Json.JsonSerializer.Serialize(x).ToLower().Contains("commentary") == false)
|
||||
// .OrderBy(x =>
|
||||
// {
|
||||
// if (Language != string.Empty)
|
||||
// {
|
||||
// args.Logger.ILog("Language: " + x.Language, x);
|
||||
// if (string.IsNullOrEmpty(x.Language))
|
||||
// return 50; // no language specified
|
||||
// if (x.Language?.ToLower() != Language)
|
||||
// return 100; // low priority not the desired language
|
||||
// }
|
||||
// return 0;
|
||||
// })
|
||||
// .ThenByDescending(x => x.Channels)
|
||||
// //.ThenBy(x => x.CodecName.ToLower() == "ac3" ? 0 : 1) // if we do this we can get commentary tracks...
|
||||
// .ThenBy(x => x.Index)
|
||||
// .FirstOrDefault();
|
||||
|
||||
bool firstAc3 = bestAudio?.Codec?.ToLower() == "ac3" && videoInfo.AudioStreams[0] == bestAudio;
|
||||
args.Logger.ILog("Best Audio: ", bestAudio == null ? (object)"null" : (object)bestAudio);
|
||||
// bool firstAc3 = bestAudio?.Codec?.ToLower() == "ac3" && videoInfo.AudioStreams[0] == bestAudio;
|
||||
// args.Logger.ILog("Best Audio: ", bestAudio == null ? (object)"null" : (object)bestAudio);
|
||||
|
||||
|
||||
string crop = args.GetParameter<string>(DetectBlackBars.CROP_KEY) ?? "";
|
||||
if (crop != string.Empty)
|
||||
crop = " -vf crop=" + crop;
|
||||
// string crop = args.GetParameter<string>(DetectBlackBars.CROP_KEY) ?? "";
|
||||
// if (crop != string.Empty)
|
||||
// crop = " -vf crop=" + crop;
|
||||
|
||||
if (ForceRencode == false && firstAc3 == true && videoH265 != null)
|
||||
{
|
||||
if (crop == string.Empty)
|
||||
{
|
||||
args.Logger.DLog("File is hevc with the first audio track being AC3");
|
||||
return 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
args.Logger.ILog("Video is hevc and ac3 but needs to be cropped");
|
||||
}
|
||||
}
|
||||
// if (ForceRencode == false && firstAc3 == true && videoH265 != null)
|
||||
// {
|
||||
// if (crop == string.Empty)
|
||||
// {
|
||||
// args.Logger.DLog("File is hevc with the first audio track being AC3");
|
||||
// return 2;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// args.Logger.ILog("Video is hevc and ac3 but needs to be cropped");
|
||||
// }
|
||||
// }
|
||||
|
||||
string ffmpegExe = GetFFMpegExe(args);
|
||||
if (string.IsNullOrEmpty(ffmpegExe))
|
||||
return -1;
|
||||
// string ffmpegExe = GetFFMpegExe(args);
|
||||
// if (string.IsNullOrEmpty(ffmpegExe))
|
||||
// return -1;
|
||||
|
||||
List<string> ffArgs = new List<string>();
|
||||
// List<string> ffArgs = new List<string>();
|
||||
|
||||
if (NvidiaEncoding == false && Threads > 0)
|
||||
ffArgs.Add($"-threads {Math.Min(Threads, 16)}");
|
||||
// if (NvidiaEncoding == false && Threads > 0)
|
||||
// ffArgs.AddRange(new[] { "-threads", Math.Min(Threads, 16).ToString() });
|
||||
|
||||
if (videoH265 == null || crop != string.Empty)
|
||||
ffArgs.Add($"-map 0:v:0 -c:v {(NvidiaEncoding ? "hevc_nvenc -preset hq" : "libx265")} -crf " + (Crf > 0 ? Crf : 21) + crop);
|
||||
else
|
||||
ffArgs.Add($"-map 0:v:0 -c:v copy");
|
||||
// if (videoH265 == null || crop != string.Empty)
|
||||
// ffArgs.AddRange(new[] { "-map", "0:v:0", "-c:v", NvidiaEncoding ? "hevc_nvenc - preset hq" : "libx265")} -crf " + (Crf > 0 ? Crf : 21) + crop);
|
||||
// "
|
||||
// else
|
||||
// ffArgs.Add($"-map 0:v:0 -c:v copy");
|
||||
|
||||
TotalTime = videoInfo.VideoStreams[0].Duration;
|
||||
// TotalTime = videoInfo.VideoStreams[0].Duration;
|
||||
|
||||
if (NormalizeAudio)
|
||||
{
|
||||
int sampleRate = bestAudio.SampleRate > 0 ? bestAudio.SampleRate : 48_000;
|
||||
ffArgs.Add($"-map 0:{bestAudio.Index} -c:a ac3 -ar {sampleRate} -af loudnorm=I=-24:LRA=7:TP=-2.0");
|
||||
}
|
||||
else if (bestAudio.Codec.ToLower() != "ac3")
|
||||
ffArgs.Add($"-map 0:{bestAudio.Index} -c:a ac3");
|
||||
else
|
||||
ffArgs.Add($"-map 0:{bestAudio.Index} -c:a copy");
|
||||
// if (NormalizeAudio)
|
||||
// {
|
||||
// int sampleRate = bestAudio.SampleRate > 0 ? bestAudio.SampleRate : 48_000;
|
||||
// ffArgs.Add($"-map 0:{bestAudio.Index} -c:a ac3 -ar {sampleRate} -af loudnorm=I=-24:LRA=7:TP=-2.0");
|
||||
// }
|
||||
// else if (bestAudio.Codec.ToLower() != "ac3")
|
||||
// ffArgs.Add($"-map 0:{bestAudio.Index} -c:a ac3");
|
||||
// else
|
||||
// ffArgs.Add($"-map 0:{bestAudio.Index} -c:a copy");
|
||||
|
||||
if (Language != string.Empty)
|
||||
ffArgs.Add($"-map 0:s:m:language:{Language}? -c:s copy");
|
||||
else
|
||||
ffArgs.Add($"-map 0:s? -c:s copy");
|
||||
|
||||
string ffArgsLine = string.Join(" ", ffArgs);
|
||||
|
||||
if (Encode(args, ffmpegExe, ffArgsLine) == false)
|
||||
return -1;
|
||||
|
||||
return 1;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
args.Logger.ELog("Failed processing VideoFile: " + ex.Message);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if (Language != string.Empty)
|
||||
// ffArgs.Add($"-map 0:s:m:language:{Language}? -c:s copy");
|
||||
// else
|
||||
// ffArgs.Add($"-map 0:s? -c:s copy");
|
||||
|
||||
|
||||
}
|
||||
// if (Encode(args, ffmpegExe, ffArgs) == false)
|
||||
// return -1;
|
||||
|
||||
// return 1;
|
||||
// }
|
||||
// catch (Exception ex)
|
||||
// {
|
||||
// args.Logger.ELog("Failed processing VideoFile: " + ex.Message);
|
||||
// return -1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
//}
|
||||
Reference in New Issue
Block a user