FF-485: Created new flow element FFmpeg Builder: Pre-Execute

This commit is contained in:
John Andrews
2023-06-17 11:06:47 +12:00
parent 47d4c912c2
commit 27093a7490
5 changed files with 385 additions and 266 deletions
@@ -3,304 +3,314 @@ using FileFlows.VideoNodes.FfmpegBuilderNodes.Models;
using System.Runtime.InteropServices;
using FileFlows.VideoNodes.Helpers;
namespace FileFlows.VideoNodes.FfmpegBuilderNodes
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
public class FfmpegBuilderExecutor: FfmpegBuilderNode
{
public class FfmpegBuilderExecutor: FfmpegBuilderNode
public override string Icon => "far fa-file-video";
public override int Inputs => 1;
public override int Outputs => 2;
public override FlowElementType Type => FlowElementType.BuildEnd;
public override string HelpUrl => "https://docs.fileflows.com/plugins/video-nodes/ffmpeg-builder";
public override bool NoEditorOnAdd => true;
[DefaultValue(true)]
[Boolean(1)]
public bool HardwareDecoding { get; set; }
public override int Execute(NodeParameters args)
{
public override string Icon => "far fa-file-video";
public override int Inputs => 1;
public override int Outputs => 2;
public override FlowElementType Type => FlowElementType.BuildEnd;
public override string HelpUrl => "https://docs.fileflows.com/plugins/video-nodes/ffmpeg-builder";
public override bool NoEditorOnAdd => true;
[DefaultValue(true)]
[Boolean(1)]
public bool HardwareDecoding { get; set; }
public override int Execute(NodeParameters args)
var model = this.Model;
if (model == null)
{
var model = this.Model;
if (model == null)
{
args.Logger.ELog("FFMPEG Builder model is null");
return -1;
}
else if (model.VideoInfo == null)
{
args.Logger.ELog("FFMPEG Builder VideoInfo is null");
return -1;
}
else if (model.VideoInfo.FileName == null)
{
args.Logger.ELog("FFMPEG Builder VideoInfo Filename is null");
return -1;
}
List<string> ffArgs = new List<string>();
args.Logger.ELog("FFMPEG Builder model is null");
return -1;
}
else if (model.VideoInfo == null)
{
args.Logger.ELog("FFMPEG Builder VideoInfo is null");
return -1;
}
else if (model.VideoInfo.FileName == null)
{
args.Logger.ELog("FFMPEG Builder VideoInfo Filename is null");
return -1;
}
List<string> ffArgs = new List<string>();
if (model.CustomParameters?.Any() == true)
ffArgs.AddRange(model.CustomParameters);
if (model.CustomParameters?.Any() == true)
ffArgs.AddRange(model.CustomParameters);
bool hasChange = false;
int actualIndex = 0;
int overallIndex = 0;
int currentType = 0;
bool hasChange = false;
int actualIndex = 0;
int overallIndex = 0;
int currentType = 0;
string sourceExtension = model.VideoInfo.FileName.Substring(model.VideoInfo.FileName.LastIndexOf(".") + 1).ToLower();
string extension = (model.Extension?.EmptyAsNull() ?? "mkv").ToLower();
string sourceExtension = model.VideoInfo.FileName.Substring(model.VideoInfo.FileName.LastIndexOf(".") + 1).ToLower();
string extension = (model.Extension?.EmptyAsNull() ?? "mkv").ToLower();
foreach (var item in model.VideoStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 1, list: model.VideoStreams.Select(x => (FfmpegStream)x).ToList())).Union(
model.AudioStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 2, list: model.AudioStreams.Select(x => (FfmpegStream)x).ToList()))).Union(
model.SubtitleStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 3, list: model.SubtitleStreams.Select(x => (FfmpegStream)x).ToList()))))
{
if (item.stream.Deleted)
{
hasChange = true;
continue;
}
if (currentType != item.type)
{
actualIndex = 0;
currentType = item.type;
}
VideoFileStream vfs = item.stream is FfmpegVideoStream ? ((FfmpegVideoStream)item.stream).Stream :
item.stream is FfmpegAudioStream ? ((FfmpegAudioStream)item.stream).Stream :
((FfmpegSubtitleStream)item.stream).Stream;
var streamArgs = item.stream.GetParameters(new ()
{
Logger = args.Logger,
OutputOverallIndex = overallIndex,
OutputTypeIndex = actualIndex,
SourceExtension = sourceExtension,
DestinationExtension = extension,
UpdateDefaultFlag = item.list.Any(x => x.Deleted == false && x.IsDefault)
});
for (int i = 0; i < streamArgs.Length; i++)
{
streamArgs[i] = streamArgs[i].Replace("{sourceTypeIndex}", vfs.TypeIndex.ToString());
streamArgs[i] = streamArgs[i].Replace("{index}", actualIndex.ToString());
}
ffArgs.AddRange(streamArgs);
hasChange |= item.stream.HasChange | item.stream.ForcedChange;
++actualIndex;
++overallIndex;
}
if (model.MetadataParameters?.Any() == true)
foreach (var item in model.VideoStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 1, list: model.VideoStreams.Select(x => (FfmpegStream)x).ToList())).Union(
model.AudioStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 2, list: model.AudioStreams.Select(x => (FfmpegStream)x).ToList()))).Union(
model.SubtitleStreams.Select((x, index) => (stream: (FfmpegStream)x, index, type: 3, list: model.SubtitleStreams.Select(x => (FfmpegStream)x).ToList()))))
{
if (item.stream.Deleted)
{
hasChange = true;
ffArgs.AddRange(model.MetadataParameters);
continue;
}
if (currentType != item.type)
{
actualIndex = 0;
currentType = item.type;
}
if (model.ForceEncode == false && hasChange == false && (string.IsNullOrWhiteSpace(model.Extension) || args.WorkingFile.ToLower().EndsWith("." + model.Extension.ToLower())))
return 2; // nothing to do
VideoFileStream vfs = item.stream is FfmpegVideoStream ? ((FfmpegVideoStream)item.stream).Stream :
item.stream is FfmpegAudioStream ? ((FfmpegAudioStream)item.stream).Stream :
((FfmpegSubtitleStream)item.stream).Stream;
List<string> startArgs = new List<string>();
if (model.InputFiles?.Any() == false)
model.InputFiles.Add(new InputFile(args.WorkingFile));
else
model.InputFiles[0].FileName = args.WorkingFile;
startArgs.AddRange(new[] { "-fflags", "+genpts" }); //Generate missing PTS if DTS is present.
startArgs.AddRange(new[] {
"-probesize", VideoInfoHelper.ProbeSize + "M"
var streamArgs = item.stream.GetParameters(new ()
{
Logger = args.Logger,
OutputOverallIndex = overallIndex,
OutputTypeIndex = actualIndex,
SourceExtension = sourceExtension,
DestinationExtension = extension,
UpdateDefaultFlag = item.list.Any(x => x.Deleted == false && x.IsDefault)
});
bool useHardwareEncoding = HardwareDecoding;
if (Environment.GetEnvironmentVariable("HW_OFF") == "1")
useHardwareEncoding = false;
if (useHardwareEncoding)
for (int i = 0; i < streamArgs.Length; i++)
{
startArgs.AddRange(GetHardwareDecodingArgs());
}
if (ffArgs.Any(x => x.Contains("vaapi") && Helpers.VaapiHelper.VaapiLinux))
{
startArgs.Add("-vaapi_device");
startArgs.Add(VaapiHelper.VaapiRenderDevice);
streamArgs[i] = streamArgs[i].Replace("{sourceTypeIndex}", vfs.TypeIndex.ToString());
streamArgs[i] = streamArgs[i].Replace("{index}", actualIndex.ToString());
}
foreach (var file in model.InputFiles)
{
startArgs.Add("-i");
startArgs.Add(file.FileName);
}
startArgs.Add("-y");
if (extension.ToLower() == "mp4" && ffArgs.IndexOf("-movflags") < 0 && startArgs.IndexOf("-movflgs") < 0)
{
startArgs.AddRange(new[] { "-movflags", "+faststart" });
}
ffArgs = startArgs.Concat(ffArgs).ToList();
// FF-378: keep attachments (fonts etc)
ffArgs.AddRange(new[] { "-map", "0:t?", "-c:t", "copy" });
var ffmpeg = FFMPEG;
// string strFfArgs = string.Join(" ", ffArgs);
// if ((strFfArgs.Contains("libaom-av1") || strFfArgs.Contains("libsvtav1")))
// {
// args.Logger.DLog("Using AV1");
// if (File.Exists(ffmpeg + "-av1"))
// {
// ffmpeg = ffmpeg + "-av1";
// if(ffArgs.IndexOf("-hwaccel") > 0)
// {
// ffArgs.RemoveRange(ffArgs.IndexOf("-hwaccel"), 2);
// if(ffArgs.IndexOf("-hwaccel_output_format") > 0)
// {
// ffArgs.RemoveRange(ffArgs.IndexOf("-hwaccel_output_format"), 2);
// }
// }
// }
// else
// args.Logger.DLog("Did not find custom FFMPEG AV1: " + ffmpeg + "-av1");
// }
if (Encode(args, ffmpeg, ffArgs, extension, dontAddInputFile: true) == false)
return -1;
foreach (var file in model.InputFiles)
{
if (file.DeleteAfterwards)
{
if (File.Exists(file.FileName) == false)
continue;
args.Logger.ILog("Deleting file: " + file.FileName);
try
{
File.Delete(file.FileName);
}
catch (Exception ex)
{
args.Logger.WLog("Failed to delete file: " + ex.Message);
}
}
}
return 1;
ffArgs.AddRange(streamArgs);
hasChange |= item.stream.HasChange | item.stream.ForcedChange;
++actualIndex;
++overallIndex;
}
internal string[] GetHardwareDecodingArgs()
if (model.MetadataParameters?.Any() == true)
{
string testFile = Path.Combine(Args.TempPath, Guid.NewGuid() + ".hwtest.mkv");
var video = this.Model.VideoStreams.Where(x => x.Stream.IsImage == false).FirstOrDefault();
if (string.IsNullOrWhiteSpace(video?.Stream?.Codec))
return new string[] { };
bool isH264 = video.Stream.Codec.Contains("264");
bool isHevc = video.Stream.Codec.Contains("265") || video.Stream.Codec.ToLower().Contains("hevc");
hasChange = true;
ffArgs.AddRange(model.MetadataParameters);
}
var decoders = isH264 ? Decoders_h264() :
isHevc ? Decoders_hevc() :
Decoders_Default();
try
if (model.ForceEncode == false && hasChange == false && (string.IsNullOrWhiteSpace(model.Extension) || args.WorkingFile.ToLower().EndsWith("." + model.Extension.ToLower())))
return 2; // nothing to do
List<string> startArgs = new List<string>();
if (model.InputFiles?.Any() == false)
model.InputFiles.Add(new InputFile(args.WorkingFile));
else
model.InputFiles[0].FileName = args.WorkingFile;
startArgs.AddRange(new[] { "-fflags", "+genpts" }); //Generate missing PTS if DTS is present.
startArgs.AddRange(new[] {
"-probesize", VideoInfoHelper.ProbeSize + "M"
});
bool useHardwareEncoding = HardwareDecoding;
if (Environment.GetEnvironmentVariable("HW_OFF") == "1")
useHardwareEncoding = false;
if (useHardwareEncoding)
{
startArgs.AddRange(GetHardwareDecodingArgs());
}
if (ffArgs.Any(x => x.Contains("vaapi") && Helpers.VaapiHelper.VaapiLinux))
{
startArgs.Add("-vaapi_device");
startArgs.Add(VaapiHelper.VaapiRenderDevice);
}
foreach (var file in model.InputFiles)
{
startArgs.Add("-i");
startArgs.Add(file.FileName);
}
startArgs.Add("-y");
if (extension.ToLower() == "mp4" && ffArgs.IndexOf("-movflags") < 0 && startArgs.IndexOf("-movflgs") < 0)
{
startArgs.AddRange(new[] { "-movflags", "+faststart" });
}
ffArgs = startArgs.Concat(ffArgs).ToList();
// FF-378: keep attachments (fonts etc)
ffArgs.AddRange(new[] { "-map", "0:t?", "-c:t", "copy" });
var ffmpeg = FFMPEG;
// string strFfArgs = string.Join(" ", ffArgs);
// if ((strFfArgs.Contains("libaom-av1") || strFfArgs.Contains("libsvtav1")))
// {
// args.Logger.DLog("Using AV1");
// if (File.Exists(ffmpeg + "-av1"))
// {
// ffmpeg = ffmpeg + "-av1";
// if(ffArgs.IndexOf("-hwaccel") > 0)
// {
// ffArgs.RemoveRange(ffArgs.IndexOf("-hwaccel"), 2);
// if(ffArgs.IndexOf("-hwaccel_output_format") > 0)
// {
// ffArgs.RemoveRange(ffArgs.IndexOf("-hwaccel_output_format"), 2);
// }
// }
// }
// else
// args.Logger.DLog("Did not find custom FFMPEG AV1: " + ffmpeg + "-av1");
// }
if(string.IsNullOrWhiteSpace(model.PreExecuteCode) == false)
{
var preExecutor = new PreExecutor(args, model.PreExecuteCode, ffArgs);
if (preExecutor.Run() == false)
return -1;
if (preExecutor.Args?.Any() == true && string.Join(" ", ffArgs) != string.Join(" ", preExecutor.Args))
{
foreach (var hw in decoders)
{
if (hw == null)
continue;
if (CanUseHardwareEncoding.DisabledByVariables(Args, hw))
continue;
try
{
var arguments = new List<string>()
{
"-y",
};
arguments.AddRange(hw);
arguments.AddRange(new[]
{
"-f", "lavfi",
"-i", "color=color=red",
"-frames:v", "10",
testFile
});
var result = Args.Execute(new ExecuteArgs
{
Command = FFMPEG,
ArgumentList = arguments.ToArray()
});
if (result.ExitCode == 0)
{
Args.Logger?.ILog("Supported hardware decoding detected: " + string.Join(" ", hw));
return hw;
}
}
catch (Exception) { }
}
Args.Logger?.ILog("No hardware decoding availble");
return new string[] { };
args.Logger.ILog("Pre-Executor updated FFmpeg Arguments!");
ffArgs = preExecutor.Args;
}
finally
}
if (Encode(args, ffmpeg, ffArgs, extension, dontAddInputFile: true) == false)
return -1;
foreach (var file in model.InputFiles)
{
if (file.DeleteAfterwards)
{
if (File.Exists(file.FileName) == false)
continue;
args.Logger.ILog("Deleting file: " + file.FileName);
try
{
if (File.Exists(testFile))
File.Delete(testFile);
File.Delete(file.FileName);
}
catch (Exception ex)
{
args.Logger.WLog("Failed to delete file: " + ex.Message);
}
}
}
return 1;
}
internal string[] GetHardwareDecodingArgs()
{
string testFile = Path.Combine(Args.TempPath, Guid.NewGuid() + ".hwtest.mkv");
var video = this.Model.VideoStreams.Where(x => x.Stream.IsImage == false).FirstOrDefault();
if (string.IsNullOrWhiteSpace(video?.Stream?.Codec))
return new string[] { };
bool isH264 = video.Stream.Codec.Contains("264");
bool isHevc = video.Stream.Codec.Contains("265") || video.Stream.Codec.ToLower().Contains("hevc");
var decoders = isH264 ? Decoders_h264() :
isHevc ? Decoders_hevc() :
Decoders_Default();
try
{
foreach (var hw in decoders)
{
if (hw == null)
continue;
if (CanUseHardwareEncoding.DisabledByVariables(Args, hw))
continue;
try
{
var arguments = new List<string>()
{
"-y",
};
arguments.AddRange(hw);
arguments.AddRange(new[]
{
"-f", "lavfi",
"-i", "color=color=red",
"-frames:v", "10",
testFile
});
var result = Args.Execute(new ExecuteArgs
{
Command = FFMPEG,
ArgumentList = arguments.ToArray()
});
if (result.ExitCode == 0)
{
Args.Logger?.ILog("Supported hardware decoding detected: " + string.Join(" ", hw));
return hw;
}
}
catch (Exception) { }
}
Args.Logger?.ILog("No hardware decoding availble");
return new string[] { };
}
private static readonly bool IsMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
private string[][] Decoders_h264()
finally
{
return new[]
try
{
//new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "cuda" }, // this fails with Impossible to convert between the formats supported by the filter 'Parsed_crop_0' and the filter 'auto_scale_0'
IsMac ? new [] { "-hwaccel", "videotoolbox" } : null,
new [] { "-hwaccel", "cuda" },
new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
new [] { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" },
new [] { "-hwaccel", "dxva2" },
new [] { "-hwaccel", "d3d11va" },
new [] { "-hwaccel", "opencl" },
};
if (File.Exists(testFile))
File.Delete(testFile);
}
catch (Exception) { }
}
}
private string[][] Decoders_hevc()
private static readonly bool IsMac = RuntimeInformation.IsOSPlatform(OSPlatform.OSX);
private string[][] Decoders_h264()
{
return new[]
{
return new[]
{
//new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "cuda" }, // this fails with Impossible to convert between the formats supported by the filter 'Parsed_crop_0' and the filter 'auto_scale_0'
IsMac ? new [] { "-hwaccel", "videotoolbox" } : null,
new [] { "-hwaccel", "cuda" },
new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
new [] { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" },
new [] { "-hwaccel", "dxva2" },
new [] { "-hwaccel", "d3d11va" },
new [] { "-hwaccel", "opencl" },
};
}
//new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "cuda" }, // this fails with Impossible to convert between the formats supported by the filter 'Parsed_crop_0' and the filter 'auto_scale_0'
IsMac ? new [] { "-hwaccel", "videotoolbox" } : null,
new [] { "-hwaccel", "cuda" },
new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
new [] { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" },
new [] { "-hwaccel", "dxva2" },
new [] { "-hwaccel", "d3d11va" },
new [] { "-hwaccel", "opencl" },
};
}
private string[][] Decoders_Default()
private string[][] Decoders_hevc()
{
return new[]
{
return new[]
{
//new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "cuda" }, // this fails with Impossible to convert between the formats supported by the filter 'Parsed_crop_0' and the filter 'auto_scale_0'
new [] { "-hwaccel", "cuda" },
new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
new [] { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" },
new [] { "-hwaccel", "dxva2" },
new [] { "-hwaccel", "d3d11va" },
new [] { "-hwaccel", "opencl" },
};
}
//new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "cuda" }, // this fails with Impossible to convert between the formats supported by the filter 'Parsed_crop_0' and the filter 'auto_scale_0'
IsMac ? new [] { "-hwaccel", "videotoolbox" } : null,
new [] { "-hwaccel", "cuda" },
new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
new [] { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" },
new [] { "-hwaccel", "dxva2" },
new [] { "-hwaccel", "d3d11va" },
new [] { "-hwaccel", "opencl" },
};
}
private string[][] Decoders_Default()
{
return new[]
{
//new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "cuda" }, // this fails with Impossible to convert between the formats supported by the filter 'Parsed_crop_0' and the filter 'auto_scale_0'
new [] { "-hwaccel", "cuda" },
new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
new [] { "-hwaccel", "vaapi", "-hwaccel_output_format", "vaapi" },
new [] { "-hwaccel", "dxva2" },
new [] { "-hwaccel", "d3d11va" },
new [] { "-hwaccel", "opencl" },
};
}
}
@@ -0,0 +1,40 @@
using FileFlows.VideoNodes.FfmpegBuilderNodes.Models;
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
/// <summary>
/// Flow element that can alter the command line arguments that the FFmpeg Builder will run
/// </summary>
public class FfmpegBuilderPreExecute : FfmpegBuilderNode
{
/// <summary>
/// Gets the Help URL for this node
/// </summary>
public override string HelpUrl => "https://docs.fileflows.com/plugins/video-nodes/ffmpeg-builder/pre-execute";
/// <summary>
/// Gets the icon for this node
/// </summary>
public override string Icon => "fas fa-mortar-pestle";
/// <summary>
/// Gets the number of outputs for this element
/// </summary>
public override int Outputs => 1;
[Required]
[DefaultValue("// Custom javascript code that runs just before the FFmpeg Builder: Executor executes the FFmpeg process.\n// Here you can alter FFmpeg parameters etc. See Help for more information.")]
[Code(1)]
public string Code { get; set; }
/// <summary>
/// Executes the node
/// </summary>
/// <param name="args">the node parameters</param>
/// <returns>the output number to execute next</returns>
public override int Execute(NodeParameters args)
{
this.Model.PreExecuteCode = this.Code;
return 1;
}
}
@@ -52,6 +52,11 @@
/// Gets or sets if the builder should forcable execute even if nothing appears to have changed
/// </summary>
public bool ForceEncode { get; set; }
/// <summary>
/// Gets or sets the code to run prior to FFmpeg Executing
/// </summary>
public string PreExecuteCode { get; set; }
/// <summary>
/// Gets or sets the video information for this video file
@@ -0,0 +1,54 @@
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
/// <summary>
/// The FFmpeg Builder PreExecutor
/// </summary>
public class PreExecutor
{
private readonly string code;
private readonly NodeParameters args;
/// <summary>
/// Gets or sets the FFmpeg Arguments
/// </summary>
public List<string> Args { get; set; }
/// <summary>
/// Constructs an instance of the pre-executor
/// </summary>
/// <param name="args">the node parameters</param>
/// <param name="code">the code to run</param>
/// <param name="ffArgs">the current FFmpeg arguments</param>
public PreExecutor(NodeParameters args, string code, List<string> ffArgs)
{
this.args = args;
this.code = code;
this.Args = ffArgs;
}
/// <summary>
/// Runs the pre-executor
/// </summary>
/// <returns>if the execution was successful</returns>
public bool Run()
{
try
{
int exitCode = args.ScriptExecutor.Execute(new FileFlows.Plugin.Models.ScriptExecutionArgs
{
Args = args,
Code = code + "\n\n// automatically added return code\nreturn 1;",
AdditionalArguments = new ()
{
{ "FFmpeg", this }
}
});
args.Logger.ILog("PreExecute Exit Code: " + exitCode);
return exitCode >= 0;
}
catch (Exception ex)
{
args.Logger?.ELog("Failed executing pre-executor: " + ex.Message + Environment.NewLine + ex.StackTrace);
return false;
}
}
}
+15 -5
View File
@@ -359,30 +359,40 @@
"CroppingThreshold-Help": "The amount of pixels that must be greater than to crop. E.g. if there's only 5 pixels detected as black space, you may consider this too small to crop."
}
},
"FfmpegBuilderPreExecute": {
"Label": "FFMPEG Buidler: Pre-Execute",
"Description": "This element lets you run custom code just prior to the [FFmpeg Builder: Executor](https://docs.fileflows.com/plugins/video-nodes/ffmpeg-builder) executes FFmpeg.\n\nThis allows you to alter the arguments passed into FFmpeg.",
"Outputs": {
"1": "Pre-Execute set"
},
"Fields": {
"Code": "Code"
}
},
"FfmpegBuilderRemuxToMkv": {
"Label": "FFMPEG Builder: Remux to MKV",
"Descritption": "Remuxes a video file into a MKV container.",
"Description": "Remuxes a video file into a MKV container.",
"Outputs": {
"1": "FFMPEG Builder set to remux to MKV"
}
},
"FfmpegBuilderRemuxToMP4": {
"Label": "FFMPEG Builder: Remux to MP4",
"Descritption": "Remuxes a video file into a MP4 container.",
"Description": "Remuxes a video file into a MP4 container.",
"Outputs": {
"1": "FFMPEG Builder set to remux to MP4"
}
},
"FfmpegBuilderRemuxToMov": {
"Label": "FFMPEG Builder: Remux to MOV",
"Descritption": "Remuxes a video file into a MOV container.",
"Description": "Remuxes a video file into a MOV container.",
"Outputs": {
"1": "FFMPEG Builder set to remux to MOV"
}
},
"FfmpegBuilderRemuxToWebm": {
"Label": "FFMPEG Builder: Remux to WEBM",
"Descritption": "Remuxes a video file into a WEGM container.",
"Description": "Remuxes a video file into a WEGM container.",
"Outputs": {
"1": "FFMPEG Builder set to remux to WEGM"
}
@@ -480,7 +490,7 @@
}
},
"ReadVideoInfo": {
"Descritption": "Reads the video information from the current working file and updates the vi variables.",
"Description": "Reads the video information from the current working file and updates the vi variables.",
"Outputs": {
"1": "File was a video file and information read into flow",
"2": "File was not a video file or failed to be read"