FF-1279 - added MXF container

This commit is contained in:
John Andrews
2024-02-14 20:44:43 +13:00
parent 31e848b60d
commit 2ec7ec3641
11 changed files with 169 additions and 21 deletions

View File

@@ -27,17 +27,64 @@ public class FfmpegBuilderAudioConverter : FfmpegBuilderNode
{
new () { Label = "AAC", Value = "aac"},
new () { Label = "AC3", Value = "ac3"},
new () { Label = "DTS", Value = "dts" },
new () { Label = "EAC3", Value = "eac3" },
new () { Label = "FLAC", Value ="flac" },
new () { Label = "MP3", Value = "mp3"},
new () { Label = "PCM", Value ="pcm" },
new () { Label = "OPUS", Value = "opus"},
new () { Label = "Vorbis", Value ="libvorbis" },
};
}
return _CodecOptions;
}
}
[DefaultValue("pcm_s16le")]
[Select(nameof(PcmFormats), 2)]
[ConditionEquals(nameof(Codec), "pcm")]
public string PcmFormat { get; set; }
private static List<ListOption> _PcmFormats;
public static List<ListOption> PcmFormats
{
get
{
if (_PcmFormats == null)
{
_PcmFormats = new List<ListOption>
{
new () { Label = "Common", Value = "###GROUP###" },
new () { Label = "Signed 16-bit Little Endian", Value = "pcm_s16le" },
new () { Label = "Signed 24-bit Little Endian", Value = "pcm_s24le"},
new () { Label = "Signed", Value = "###GROUP###" },
new () { Label = "8-bit", Value = "pcm_s8"},
new () { Label = "16-bit Little Endian", Value = "pcm_s16le" },
new () { Label = "16-bit Big Endian", Value = "pcm_s16be"},
new () { Label = "24-bit Little Endian", Value = "pcm_s24le"},
new () { Label = "24-bit Big Endian", Value = "pcm_s24be"},
new () { Label = "32-bit Little Endian", Value = "pcm_s32le"},
new () { Label = "32-bit Big Endian", Value = "pcm_s32be"},
new () { Label = "64-bit Little Endian", Value = "pcm_s64le"},
new () { Label = "64-bit Big Endian", Value = "pcm_s64be"},
new () { Label = "Floating-point", Value = "###GROUP###" },
new () { Label = "32-bit Little Endian", Value = "pcm_f32le"},
new () { Label = "32-bit Big Endian", Value = "pcm_f32be"},
new () { Label = "16-bit Little Endian", Value = "pcm_f16le"},
new () { Label = "24-bit Little Endian", Value = "pcm_f24le"},
new () { Label = "64-bit Little Endian", Value = "pcm_f64le"},
new () { Label = "64-bit Big Endian", Value = "pcm_f64be"},
};
}
return _PcmFormats;
}
}
[DefaultValue(0)]
[Select(nameof(ChannelsOptions), 2)]
[Select(nameof(ChannelsOptions), 3)]
public float Channels { get; set; }
private static List<ListOption> _ChannelsOptions;
@@ -60,7 +107,7 @@ public class FfmpegBuilderAudioConverter : FfmpegBuilderNode
}
}
[Select(nameof(BitrateOptions), 3)]
[Select(nameof(BitrateOptions), 4)]
public int Bitrate { get; set; }
private static List<ListOption> _BitrateOptions;
@@ -85,13 +132,13 @@ public class FfmpegBuilderAudioConverter : FfmpegBuilderNode
}
[TextVariable(4)]
[TextVariable(5)]
public string Pattern { get; set; }
[Boolean(5)]
[Boolean(6)]
public bool NotMatching { get; set; }
[Boolean(6)]
[Boolean(7)]
public bool UseLanguageCode { get; set; }
public override int Execute(NodeParameters args)
@@ -157,7 +204,11 @@ public class FfmpegBuilderAudioConverter : FfmpegBuilderNode
/// <returns>if the stream had to be converted or not</returns>
private bool ConvertTrack(NodeParameters args, FfmpegAudioStream stream)
{
bool codecSame = stream.Stream.Codec?.ToLower() == Codec?.ToLower();
string codec = Codec?.ToLowerInvariant() ?? string.Empty;
if (codec == "pcm")
codec = PcmFormat;
bool codecSame = stream.Stream.Codec?.ToLowerInvariant() == codec;
bool channelsSame = Channels == 0 || Math.Abs(Channels - stream.Stream.Channels) < 0.05f;
bool bitrateSame = Bitrate < 2 || stream.Stream.Bitrate == 0 ||
Math.Abs(stream.Stream.Bitrate - Bitrate) < 0.05f;
@@ -170,7 +221,7 @@ public class FfmpegBuilderAudioConverter : FfmpegBuilderNode
stream.Codec = Codec.ToLowerInvariant();
stream.EncodingParameters.AddRange(FfmpegBuilderAudioAddTrack.GetNewAudioTrackParameters(args, stream, Codec, Channels, Bitrate, 0));
stream.EncodingParameters.AddRange(FfmpegBuilderAudioAddTrack.GetNewAudioTrackParameters(args, stream, codec, Channels, Bitrate, 0));
return true;
}
}

View File

@@ -145,7 +145,13 @@ public class FfmpegBuilderTrackSorter : FfmpegBuilderNode
if (changed)
{
streams[i].ForcedChange = true;
orderedStreams[i].ForcedChange = true;
args.Logger?.ILog("Stream has change[1]: " + streams[i]);
if (streams[i] != orderedStreams[i])
{
orderedStreams[i].ForcedChange = true;
args.Logger?.ILog("Stream has change[2]: " + orderedStreams[i]);
}
}
changes |= changed;
streams[i] = orderedStreams[i];

View File

@@ -98,8 +98,8 @@
/// <returns>the string representation of stream</returns>
public override string ToString()
{
if (Stream != null)
return Stream.ToString() + (Deleted ? " / Deleted" : "");
// if (Stream != null)
// return Stream.ToString() + (Deleted ? " / Deleted" : "");
// can be null in unit tests
return string.Join(" / ", new string[]
{
@@ -108,7 +108,9 @@
Codec,
Title,
Channels > 0 ? Channels.ToString("0.0") : null,
Deleted ? "Deleted" : null
IsDefault ? "Default" : null,
Deleted ? "Deleted" : null,
HasChange ? "Changed" : null
}.Where(x => string.IsNullOrWhiteSpace(x) == false));
}
}

View File

@@ -97,6 +97,7 @@
Language = item.stream.Language,
Stream = item.stream,
Channels = item.stream.Channels,
IsDefault = item.stream.Default,
Codec = item.stream.Codec
});
}
@@ -115,9 +116,15 @@
}
if(info.FileName.ToLower().EndsWith(".mp4"))
model.Extension = info.FileName.Substring(info.FileName.LastIndexOf(".") + 1);
if (info.FileName.ToLower().EndsWith(".mkv"))
model.Extension = info.FileName.Substring(info.FileName.LastIndexOf(".") + 1);
model.Extension = info.FileName[(info.FileName.LastIndexOf(".", StringComparison.Ordinal) + 1)..];
else if (info.FileName.ToLower().EndsWith(".mkv"))
model.Extension = info.FileName[(info.FileName.LastIndexOf(".", StringComparison.Ordinal) + 1)..];
else if (info.FileName.ToLower().EndsWith(".mov"))
model.Extension = info.FileName[(info.FileName.LastIndexOf(".", StringComparison.Ordinal) + 1)..];
else if (info.FileName.ToLower().EndsWith(".mxf"))
model.Extension = info.FileName[(info.FileName.LastIndexOf(".", StringComparison.Ordinal) + 1)..];
else if (info.FileName.ToLower().EndsWith(".webm"))
model.Extension = info.FileName[(info.FileName.LastIndexOf(".", StringComparison.Ordinal) + 1)..];
return model;
}

View File

@@ -91,8 +91,8 @@ public class FfmpegSubtitleStream : FfmpegStream
/// <returns>the string representation of stream</returns>
public override string ToString()
{
if (Stream != null)
return Stream.ToString() + (Deleted ? " / Deleted" : "");
// if (Stream != null)
// return Stream.ToString() + (Deleted ? " / Deleted" : "");
// can be null in unit tests
return string.Join(" / ", new string[]
@@ -101,7 +101,9 @@ public class FfmpegSubtitleStream : FfmpegStream
Language,
Codec,
Title,
Deleted ? "Deleted" : null
IsDefault ? "Default" : null,
Deleted ? "Deleted" : null,
HasChange ? "Changed" : null
}.Where(x => string.IsNullOrWhiteSpace(x) == false));
}
}

View File

@@ -120,8 +120,8 @@ public class FfmpegVideoStream : FfmpegStream
/// <returns>the string representation of stream</returns>
public override string ToString()
{
if (Stream != null)
return Stream.ToString() + (Deleted ? " / Deleted" : "");
// if (Stream != null)
// return Stream.ToString() + (Deleted ? " / Deleted" : "");
// can be null in unit tests
return string.Join(" / ", new string[]
@@ -129,7 +129,8 @@ public class FfmpegVideoStream : FfmpegStream
Index.ToString(),
Codec,
Title,
Deleted ? "Deleted" : null
Deleted ? "Deleted" : null,
HasChange ? "Changed" : null
}.Where(x => string.IsNullOrWhiteSpace(x) == false));
}
}

View File

@@ -0,0 +1,24 @@
namespace FileFlows.VideoNodes.FfmpegBuilderNodes;
/// <summary>
/// Remuxes a file to the MXF container
/// </summary>
public class FfmpegBuilderRemuxToMxf : FfmpegBuilderNode
{
/// <summary>
/// Gets or sets the URL to the helper page
/// </summary>
public override string HelpUrl => "https://fileflows.com/docs/plugins/video-nodes/ffmpeg-builder/remux-to-mxf";
/// <summary>
/// Gets that this is an enterprise flow element
/// </summary>
public override bool Enterprise => true;
/// <inheritdoc />
public override int Execute(NodeParameters args)
{
this.Model.Extension = "mxf";
return 1;
}
}

View File

@@ -176,6 +176,11 @@ public class AudioStream : VideoFileStream
/// </summary>
public int SampleRate { get; set; }
/// <summary>
/// If this is a the default audio track
/// </summary>
public bool Default { get; set; }
/// <summary>
/// Converts the steam to a string
/// </summary>
@@ -187,7 +192,8 @@ public class AudioStream : VideoFileStream
Language,
Codec,
Title,
Channels > 0 ? Channels.ToString("0.0") : null
Channels > 0 ? Channels.ToString("0.0") : null,
Default ? "Default" : null
}.Where(x => string.IsNullOrWhiteSpace(x) == false));
}

View File

@@ -315,6 +315,7 @@ public class VideoInfoHelper
audio.Codec = audio.Codec[..^1].Trim();
audio.Language = GetLanguage(line);
audio.Default = info.Contains("(default)");
// if (info.IndexOf("0 channels", StringComparison.Ordinal) >= 0)
// {
// logger?.WLog("Stream contained '0 Channels'");

View File

@@ -175,6 +175,8 @@
"Fields": {
"Channels": "Channels",
"Channels-Help": "The number of channels this new audio track will be.\nIf you specify more channels than the source, FFMPEG will automatically upmix it.\nIf you specify fewer channels than the source, FFMPEG will automatically down mix it.",
"PcmFormat": "Format",
"PcmFormat-Help": "The PCM format to use for encoding PCM audio.",
"Bitrate": "Bitrate",
"Bitrate-Help": "Bitrate of the audio track",
"Codec": "Codec",
@@ -470,6 +472,8 @@
"1": "Video set to use prores"
},
"Fields": {
"Encoder": "Encoder",
"Encoder-Help": "Which FFmpeg prores encoder to use.",
"Quality": "Quality",
"Quality-Help": "0 for maximum quality, 32 for minimum quality. 9 to 13 are good values to use.",
"Profile": "Profile",
@@ -490,6 +494,13 @@
"1": "FFMPEG Builder set to remux to MKV"
}
},
"FfmpegBuilderRemuxToMxf": {
"Label": "FFMPEG Builder: Remux to MXF",
"Description": "Remuxes a video file into a MXF container.",
"Outputs": {
"1": "FFMPEG Builder set to remux to MXF"
}
},
"FfmpegBuilderRemuxToMP4": {
"Label": "FFMPEG Builder: Remux to MP4",
"Description": "Remuxes a video file into a MP4 container.",

View File

@@ -90,11 +90,48 @@ namespace FileFlows.VideoNodes
var videoInfo = new VideoInfoHelper(ffmpegExe, args.Logger).Read(outputFile);
SetVideoInfo(args, videoInfo, this.Variables ?? new Dictionary<string, object>());
}
else if (success.successs == false)
{
// look for known error messages
var lines = success.output.Split(new string[] { "\r\n", "\n" }, StringSplitOptions.RemoveEmptyEntries)
.ToArray();
if (lines.Count() >= 50) {
lines = lines.TakeLast(50).ToArray();
}
string line;
if (HasLine(lines, "is not supported by the bitstream filter", out line))
{
// get that line, more efficiently that twice
int codecIndex = line.IndexOf("Codec", StringComparison.InvariantCulture);
if (codecIndex > 0)
line = line[codecIndex..];
int periodIndex = line.IndexOf(".", StringComparison.InvariantCulture);
if (periodIndex > 0)
line = line[..periodIndex];
args.FailureReason = line;
}
else if (HasLine(lines, "codec not currently supported in container", out line))
{
args.FailureReason = "Codec not currently supported in container";
}
else if (HasLine(lines, "encoding with ProRes Proxy/LT/422/422 HQ (apco, apcs, apcn, ap4h) profile, need YUV422P10 input", out line))
{
args.FailureReason = "Encoding with ProRes Proxy/LT/422/422 HQ (apco, apcs, apcn, ap4h) profile, need YUV422P10 input";
}
}
Encoder.AtTime -= AtTimeEvent;
Encoder.OnStatChange -= EncoderOnOnStatChange;
Encoder = null;
output = success.output;
return success.successs;
bool HasLine(string[] lines, string text, out string line)
{
line = lines.FirstOrDefault(x => x.Contains(text));
return line != null;
}
}
public override Task Cancel()