mirror of
https://github.com/revenz/FileFlowsPlugins.git
synced 2026-03-01 15:30:06 -06:00
Added FFMPEG Builder Video Bitrate. Added ability to adjust probe size
This commit is contained in:
@@ -62,6 +62,9 @@ namespace FileFlows.VideoNodes.FfmpegBuilderNodes
|
||||
else
|
||||
model.InputFiles[0] = args.WorkingFile;
|
||||
|
||||
startArgs.AddRange(new[] {
|
||||
"-probesize", VideoInfoHelper.ProbeSize + "M"
|
||||
});
|
||||
|
||||
if (HardwareDecoding)
|
||||
{
|
||||
|
||||
@@ -50,7 +50,17 @@
|
||||
_OptionalEncodingParameters = value ?? new List<string>();
|
||||
}
|
||||
}
|
||||
public override bool HasChange => EncodingParameters.Any() || Filter.Any();
|
||||
private List<string> _AdditionalParameters = new List<string>();
|
||||
public List<string> AdditionalParameters
|
||||
{
|
||||
get => _AdditionalParameters;
|
||||
set
|
||||
{
|
||||
_AdditionalParameters = value ?? new List<string>();
|
||||
}
|
||||
}
|
||||
public override bool HasChange => EncodingParameters.Any() || Filter.Any() || AdditionalParameters.Any();
|
||||
|
||||
|
||||
public override string[] GetParameters(int outputIndex)
|
||||
{
|
||||
@@ -58,7 +68,7 @@
|
||||
return new string[] { };
|
||||
|
||||
var results = new List<string> { "-map", "0:v:" + outputIndex };
|
||||
if (Filter.Any() == false && EncodingParameters.Any() == false)
|
||||
if (Filter.Any() == false && EncodingParameters.Any() == false && AdditionalParameters.Any() == false)
|
||||
{
|
||||
results.Add("-c:v:" + Stream.TypeIndex);
|
||||
results.Add("copy");
|
||||
@@ -78,6 +88,8 @@
|
||||
// we need to set this codec since a filter will be applied, so we cant copy it.
|
||||
//results.Add("copy");
|
||||
}
|
||||
if (AdditionalParameters.Any())
|
||||
results.AddRange(AdditionalParameters.Select(x => x.Replace("{index}", outputIndex.ToString())));
|
||||
|
||||
if (Filter.Any() || OptionalFilter.Any())
|
||||
{
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
|
||||
|
||||
public class FfmpegBuilderVideoBitrate : FfmpegBuilderNode
|
||||
{
|
||||
public override string HelpUrl => "https://github.com/revenz/FileFlows/wiki/FFMPEG-Builder:-Video-Bitrate";
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the bitrate in K
|
||||
/// </summary>
|
||||
[NumberInt(1)]
|
||||
[DefaultValue(3000)]
|
||||
public float Bitrate { get; set; }
|
||||
|
||||
|
||||
public override int Execute(NodeParameters args)
|
||||
{
|
||||
base.Init(args);
|
||||
|
||||
var video = Model.VideoStreams?.Where(x => x.Deleted == false)?.FirstOrDefault();
|
||||
if (video?.Stream == null)
|
||||
{
|
||||
args.Logger?.ELog("No video stream found");
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(Bitrate < 0)
|
||||
{
|
||||
args.Logger?.ELog("Minimum birate not set");
|
||||
return -1;
|
||||
}
|
||||
int minimum = (int)(Bitrate * 0.75f);
|
||||
int maximum = (int)(Bitrate * 1.25f);
|
||||
|
||||
float currentBitrate = (int)(video.Stream.Bitrate / 1024f);
|
||||
if(currentBitrate <= 0)
|
||||
{
|
||||
// need to work it out
|
||||
currentBitrate = (float)(args.WorkingFileSize / video.Stream.Duration.TotalSeconds);
|
||||
// rough estimate of 75% of the file is video
|
||||
currentBitrate *= 0.75f;
|
||||
}
|
||||
video.AdditionalParameters.AddRange(new[]
|
||||
{
|
||||
"-b:v:{index}", Bitrate + "k",
|
||||
"-minrate", minimum + "k",
|
||||
"-maxrate", maximum + "k",
|
||||
"-bufsize", currentBitrate + "k"
|
||||
});
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,13 @@ namespace FileFlows.VideoNodes
|
||||
public override int Outputs => 1;
|
||||
public override FlowElementType Type => FlowElementType.Input;
|
||||
|
||||
public override bool NoEditorOnAdd => true;
|
||||
|
||||
[DefaultValue(25)]
|
||||
[NumberInt(1)]
|
||||
[Range(5, 1000)]
|
||||
public int ProbeSize { get; set; }
|
||||
|
||||
private Dictionary<string, object> _Variables;
|
||||
public override Dictionary<string, object> Variables => _Variables;
|
||||
public VideoFile()
|
||||
@@ -48,6 +55,8 @@ namespace FileFlows.VideoNodes
|
||||
if (string.IsNullOrEmpty(ffmpegExe))
|
||||
return -1;
|
||||
|
||||
VideoInfoHelper.ProbeSize = this.ProbeSize;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
@@ -148,6 +148,7 @@ public class FfmpegBuilder_AddAudioTests
|
||||
Assert.AreEqual("AAC", best.Codec);
|
||||
Assert.AreEqual(2f, best.Channels);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_AddAudio_DtsMono()
|
||||
{
|
||||
|
||||
@@ -813,6 +813,107 @@ namespace FileFlows.VideoNodes.Tests.FfmpegBuilderTests
|
||||
Assert.AreEqual(1, result);
|
||||
}
|
||||
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_VideoBitrate()
|
||||
{
|
||||
const string file = @"D:\videos\unprocessed\basic.mkv";
|
||||
var logger = new TestLogger();
|
||||
const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
|
||||
var vi = new VideoInfoHelper(ffmpeg, logger);
|
||||
var vii = vi.Read(file);
|
||||
var args = new NodeParameters(file, logger, false, string.Empty);
|
||||
args.GetToolPathActual = (string tool) => ffmpeg;
|
||||
args.TempPath = @"D:\videos\temp";
|
||||
args.Parameters.Add("VideoInfo", vii);
|
||||
|
||||
|
||||
FfmpegBuilderStart ffStart = new();
|
||||
Assert.AreEqual(1, ffStart.Execute(args));
|
||||
|
||||
FfmpegBuilderVideoBitrate ffBitrate = new();
|
||||
ffBitrate.Bitrate = 1_000;
|
||||
ffBitrate.Execute(args);
|
||||
|
||||
FfmpegBuilderExecutor ffExecutor = new();
|
||||
ffExecutor.HardwareDecoding = true;
|
||||
int result = ffExecutor.Execute(args);
|
||||
|
||||
string log = logger.ToString();
|
||||
Assert.AreEqual(1, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_VideoCodecAndBitrate()
|
||||
{
|
||||
const string file = @"D:\videos\unprocessed\basic.mkv";
|
||||
var logger = new TestLogger();
|
||||
const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
|
||||
var vi = new VideoInfoHelper(ffmpeg, logger);
|
||||
var vii = vi.Read(file);
|
||||
var args = new NodeParameters(file, logger, false, string.Empty);
|
||||
args.GetToolPathActual = (string tool) => ffmpeg;
|
||||
args.TempPath = @"D:\videos\temp";
|
||||
args.Parameters.Add("VideoInfo", vii);
|
||||
|
||||
|
||||
FfmpegBuilderStart ffStart = new();
|
||||
Assert.AreEqual(1, ffStart.Execute(args));
|
||||
|
||||
FfmpegBuilderVideoCodec ffEncode = new();
|
||||
ffEncode.VideoCodec = "h264";
|
||||
ffEncode.Force = true;
|
||||
ffEncode.Execute(args);
|
||||
|
||||
FfmpegBuilderVideoBitrate ffBitrate = new();
|
||||
ffBitrate.Bitrate = 1_000;
|
||||
ffBitrate.Execute(args);
|
||||
|
||||
FfmpegBuilderExecutor ffExecutor = new();
|
||||
ffExecutor.HardwareDecoding = true;
|
||||
int result = ffExecutor.Execute(args);
|
||||
|
||||
string log = logger.ToString();
|
||||
Assert.AreEqual(1, result);
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void FfmpegBuilder_FF43()
|
||||
{
|
||||
const string file = @"D:\videos\testfiles\ff-43.ts";
|
||||
var logger = new TestLogger();
|
||||
const string ffmpeg = @"C:\utils\ffmpeg\ffmpeg.exe";
|
||||
var vi = new VideoInfoHelper(ffmpeg, logger);
|
||||
var vii = vi.Read(file);
|
||||
var args = new NodeParameters(file, logger, false, string.Empty);
|
||||
args.GetToolPathActual = (string tool) => ffmpeg;
|
||||
args.TempPath = @"D:\videos\temp";
|
||||
args.Parameters.Add("VideoInfo", vii);
|
||||
|
||||
|
||||
FfmpegBuilderStart ffStart = new();
|
||||
Assert.AreEqual(1, ffStart.Execute(args));
|
||||
|
||||
FfmpegBuilderAudioTrackRemover ffRemoveAudio = new();
|
||||
ffRemoveAudio.RemoveAll = true;
|
||||
ffRemoveAudio.Execute(args);
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio = new();
|
||||
ffAddAudio.Codec = "ac3";
|
||||
ffAddAudio.Index = 0;
|
||||
ffAddAudio.Execute(args);
|
||||
|
||||
FfmpegBuilderAudioAddTrack ffAddAudio2 = new();
|
||||
ffAddAudio2.Codec = "aac";
|
||||
ffAddAudio2.Index = 1;
|
||||
ffAddAudio2.Execute(args);
|
||||
|
||||
FfmpegBuilderExecutor ffExecutor = new();
|
||||
int result = ffExecutor.Execute(args);
|
||||
|
||||
string log = logger.ToString();
|
||||
Assert.AreEqual(1, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,21 @@ namespace FileFlows.VideoNodes
|
||||
static Regex rgxDuration2 = new Regex(@"(?<=((^[\s]+Duration:[\s])))([\d]+:?)+\.[\d]{1,7}", RegexOptions.Multiline);
|
||||
static Regex rgxAudioSampleRate = new Regex(@"(?<=((,|\s)))[\d]+(?=([\s]?hz))", RegexOptions.IgnoreCase);
|
||||
|
||||
static int _ProbeSize = 25;
|
||||
internal static int ProbeSize
|
||||
{
|
||||
get => _ProbeSize;
|
||||
set
|
||||
{
|
||||
if (value < 5)
|
||||
_ProbeSize = 5;
|
||||
else if (value > 1000)
|
||||
_ProbeSize = 1000;
|
||||
else
|
||||
_ProbeSize = value;
|
||||
}
|
||||
}
|
||||
|
||||
public VideoInfoHelper(string ffMpegExe, ILogger logger)
|
||||
{
|
||||
this.ffMpegExe = ffMpegExe;
|
||||
@@ -48,9 +63,16 @@ namespace FileFlows.VideoNodes
|
||||
process.StartInfo.RedirectStandardOutput = true;
|
||||
process.StartInfo.RedirectStandardError = true;
|
||||
process.StartInfo.CreateNoWindow = true;
|
||||
process.StartInfo.ArgumentList.Add("-hide_banner");
|
||||
process.StartInfo.ArgumentList.Add("-i");
|
||||
process.StartInfo.ArgumentList.Add(filename);
|
||||
foreach (var arg in new[]
|
||||
{
|
||||
"-hide_banner",
|
||||
"-probesize", ProbeSize + "M",
|
||||
"-i",
|
||||
filename,
|
||||
})
|
||||
{
|
||||
process.StartInfo.ArgumentList.Add(arg);
|
||||
}
|
||||
process.Start();
|
||||
string output = process.StandardError.ReadToEnd();
|
||||
output = output.Replace("At least one output file must be specified", string.Empty).Trim();
|
||||
@@ -243,14 +265,25 @@ namespace FileFlows.VideoNodes
|
||||
audio.TypeIndex = int.Parse(Regex.Match(line, @"#([\d]+):([\d]+)").Groups[2].Value) - 1;
|
||||
audio.Codec = parts[0].Substring(parts[0].IndexOf("Audio: ") + "Audio: ".Length).Trim().Split(' ').First().ToLower() ?? "";
|
||||
audio.Language = Regex.Match(line, @"(?<=(Stream\s#[\d]+:[\d]+)\()[^\)]+").Value?.ToLower() ?? "";
|
||||
//Logger.ILog("codec: " + vs.Codec);
|
||||
if (parts[2] == "stereo")
|
||||
audio.Channels = 2;
|
||||
else if (parts[2] == "mono")
|
||||
audio.Channels = 1;
|
||||
else if (Regex.IsMatch(parts[2], @"^[\d]+(\.[\d]+)?"))
|
||||
if (info.IndexOf("0 channels") >= 0)
|
||||
{
|
||||
audio.Channels = float.Parse(Regex.Match(parts[2], @"^[\d]+(\.[\d]+)?").Value);
|
||||
audio.Channels = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
//Logger.ILog("codec: " + vs.Codec);
|
||||
if (parts[2] == "stereo")
|
||||
audio.Channels = 2;
|
||||
else if (parts[2] == "mono")
|
||||
audio.Channels = 1;
|
||||
else if (Regex.IsMatch(parts[2], @"^[\d]+(\.[\d]+)?"))
|
||||
{
|
||||
audio.Channels = float.Parse(Regex.Match(parts[2], @"^[\d]+(\.[\d]+)?").Value);
|
||||
}
|
||||
}
|
||||
catch (Exception) { }
|
||||
}
|
||||
|
||||
var match = rgxAudioSampleRate.Match(info);
|
||||
|
||||
@@ -119,6 +119,11 @@
|
||||
"Description": "An input video file that has had its VideoInformation read and can be processed",
|
||||
"Outputs": {
|
||||
"1": "Video file from library"
|
||||
},
|
||||
"Fields": {
|
||||
"ProbeSize": "Probe Size",
|
||||
"ProbeSize-Suffix": "MB",
|
||||
"ProbeSize-Help": "The probe size to use in FFMPEG when executing."
|
||||
}
|
||||
},
|
||||
"DetectBlackBars": {
|
||||
@@ -373,6 +378,18 @@
|
||||
"1": "FFMPEG Builder video streams set to encode in 10 Bit"
|
||||
}
|
||||
},
|
||||
"FfmpegBuilderVideoBitrate": {
|
||||
"Label": "FFMPEG Builder: Video Bitrate",
|
||||
"Description": "Sets FFMPEG Builder to encode the video given the bitrate",
|
||||
"Outputs": {
|
||||
"1": "FFMPEG Builder video streams updated"
|
||||
},
|
||||
"Fields": {
|
||||
"Bitrate": "Bitrate",
|
||||
"Bitrate-Suffix": "KB",
|
||||
"Bitrate-Help": "The target bitrate of the video in kilobytes"
|
||||
}
|
||||
},
|
||||
"FfmpegBuilderVideoCodec": {
|
||||
"Label": "FFMPEG Builder: Video Codec",
|
||||
"Description": "Sets FFMPEG Builder to encode the video streams in the specified codec",
|
||||
|
||||
@@ -185,7 +185,7 @@ namespace FileFlows.VideoNodes
|
||||
}).Result;
|
||||
if (cmd.ExitCode != 0 || string.IsNullOrWhiteSpace(cmd.Output) == false)
|
||||
{
|
||||
args.Logger?.WLog($"Cant prcoess '{encodingParams}': {cmd.Output ?? ""}");
|
||||
args.Logger?.WLog($"Cant process '{encodingParams}': {cmd.Output ?? ""}");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user