FF-1227 - added pixel format detection to hw decoding

This commit is contained in:
John Andrews
2024-01-28 20:39:53 +13:00
parent 601fd8f566
commit c5c104ef21
7 changed files with 57 additions and 13 deletions

Binary file not shown.

Binary file not shown.

View File

@@ -192,7 +192,7 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
var video = this.Model.VideoStreams.FirstOrDefault(x => x.Stream.IsImage == false);
startArgs.AddRange(GetHardwareDecodingArgs(args, localFile, FFMPEG, video?.Stream?.Codec));
startArgs.AddRange(GetHardwareDecodingArgs(args, localFile, FFMPEG, video?.Stream?.Codec, video?.Stream?.PixelFormat));
}
}
@@ -257,7 +257,7 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
return 1;
}
internal static string[] GetHardwareDecodingArgs(NodeParameters args, string localFile, string ffmpeg, string codec)
internal static string[] GetHardwareDecodingArgs(NodeParameters args, string localFile, string ffmpeg, string codec, string pixelFormat)
{
string testFile = FileHelper.Combine(args.TempPath, Guid.NewGuid() + ".hwtest.mkv");
if (string.IsNullOrWhiteSpace(codec))
@@ -270,10 +270,13 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
Decoders_Default(args);
try
{
List<string> tested = new List<string>();
foreach (var hw in decoders)
{
if (hw == null)
continue;
if (hw.Contains("#FORMAT#") && string.IsNullOrWhiteSpace(pixelFormat))
continue;
if (CanUseHardwareEncoding.DisabledByVariables(args, hw))
{
args.Logger?.ILog("HW disabled by variables: " + string.Join(", ", hw));
@@ -286,7 +289,8 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
{
"-y",
};
arguments.AddRange(hw);
foreach(var hwarg in hw)
arguments.Add(hwarg.Replace("#FORMAT#", pixelFormat));
arguments.AddRange(new[]
{
"-i", localFile,
@@ -296,6 +300,11 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
//"-f", "null", "-",
testFile
});
string line = string.Join("", arguments);
if (tested.Contains(line))
continue; // avoids testing twice if the #FORMAT# already tested one
tested.Add(line);
DateTime dtStart = DateTime.Now;
const int timeout = 20;
@@ -361,18 +370,20 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "novideotoolbox" && x.Value as bool? == true);
bool noVulkan =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "novulkan" && x.Value as bool? == true);
bool noDxva2 =
bool noDxva2 = OperatingSystem.IsWindows() == false ||
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nodxva2" && x.Value as bool? == true);
bool noD3d11va =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nod3d11va" && x.Value as bool? == true);
bool noD3d11va = OperatingSystem.IsWindows() == false ||
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nod3d11va" && x.Value as bool? == true);
bool noOpencl =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "noopencl" && x.Value as bool? == true);
return new[]
{
noVideoToolbox == false && IsMac ? new [] { "-hwaccel", "videotoolbox" } : null,
noNvidia ? null : new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "#FORMAT#" },
noNvidia ? null : 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'
noNvidia ? null : new [] { "-hwaccel", "cuda" },
noNvidia ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "#FORMAT#" },
noQsv ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "p010le" },
noQsv ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
noQsv ? null : new [] { "-hwaccel", "qsv" },
@@ -401,17 +412,19 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
bool noVulkan =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "novulkan" && x.Value as bool? == true);
bool noDxva2 =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nodxva2" && x.Value as bool? == true);
OperatingSystem.IsWindows() == false || args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nodxva2" && x.Value as bool? == true);
bool noD3d11va =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nod3d11va" && x.Value as bool? == true);
OperatingSystem.IsWindows() == false || args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nod3d11va" && x.Value as bool? == true);
bool noOpencl =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "noopencl" && x.Value as bool? == true);
return new[]
{
noVideoToolbox == false && IsMac ? new [] { "-hwaccel", "videotoolbox" } : null,
noNvidia ? null : new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "#FORMAT#" },
noNvidia ? null : 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'
noNvidia ? null : new [] { "-hwaccel", "cuda" },
noNvidia ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "#FORMAT#" },
noQsv ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "p010le" },
noQsv ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
noQsv ? null : new [] { "-hwaccel", "qsv" },
@@ -440,17 +453,19 @@ public class FfmpegBuilderExecutor: FfmpegBuilderNode
bool noVulkan =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "novulkan" && x.Value as bool? == true);
bool noDxva2 =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nodxva2" && x.Value as bool? == true);
OperatingSystem.IsWindows() == false || args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nodxva2" && x.Value as bool? == true);
bool noD3d11va =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nod3d11va" && x.Value as bool? == true);
OperatingSystem.IsWindows() == false || args.Variables.Any(x => x.Key?.ToLowerInvariant() == "nod3d11va" && x.Value as bool? == true);
bool noOpencl =
args.Variables.Any(x => x.Key?.ToLowerInvariant() == "noopencl" && x.Value as bool? == true);
return new[]
{
noNvidia ? null : new [] { "-hwaccel", "cuda", "-hwaccel_output_format", "#FORMAT#" },
noNvidia ? null : 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'
noNvidia ? null : new [] { "-hwaccel", "cuda" },
noNvidia ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "#FORMAT#" },
noQsv ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "p010le" },
noQsv ? null : new [] { "-hwaccel", "qsv", "-hwaccel_output_format", "qsv" },
noQsv ? null : new [] { "-hwaccel", "qsv" },

View File

@@ -95,7 +95,7 @@ public class VideoHasErrors: VideoNode
{
var hardwareDecodingArgs =
FfmpegBuilderNodes.FfmpegBuilderExecutor.GetHardwareDecodingArgs(args, filename, ffmpegPath,
video?.Codec);
video?.Codec, video?.PixelFormat);
if (hardwareDecodingArgs?.Any() == true)
{
foreach (var hwArg in hardwareDecodingArgs)

View File

@@ -70,7 +70,7 @@ namespace VideoNodes.Tests
public void VideoInfoTest_AC1()
{
string ffmpegOutput =
@"Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/media/Videos/#-Test Tdarr/Input3/input file.mp4':
@"Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/media/Videos/Input3/input file.mp4':
Metadata:
major_brand : mp42
minor_version : 512
@@ -92,6 +92,7 @@ namespace VideoNodes.Tests
var vi = VideoInfoHelper.ParseOutput(null, ffmpegOutput);
Assert.AreEqual(1920, vi.VideoStreams[0].Width);
Assert.AreEqual(1080, vi.VideoStreams[0].Height);
Assert.AreEqual("yuv420p", vi.VideoStreams[0].PixelFormat);
}

View File

@@ -88,6 +88,11 @@ public class VideoFileStream
/// Gets or sets the input file index
/// </summary>
public int InputFileIndex { get; set; } = 0;
/// <summary>
/// Gets or sets the pixel format that should be used to decode this stream
/// </summary>
public string PixelFormat { get; set; }
}
/// <summary>

View File

@@ -256,6 +256,7 @@ public class VideoInfoHelper
}
vs.Codec = line.Substring(line.IndexOf("Video: ") + "Video: ".Length).Replace(",", "").Trim().Split(' ').First().ToLower();
vs.PixelFormat = GetDecoderPixelFormat(line);
var dimensions = Regex.Match(line, @"([\d]{3,})x([\d]{3,})");
if (int.TryParse(dimensions.Groups[1].Value, out int width))
vs.Width = width;
@@ -265,7 +266,7 @@ public class VideoInfoHelper
if (Regex.IsMatch(line, @"([\d]+(\.[\d]+)?)\sfps") &&
float.TryParse(Regex.Match(line, @"([\d]+(\.[\d]+)?)\sfps").Groups[1].Value, out float fps))
{
logger.ILog("Frames Per Second: " + fps);
logger?.ILog("Frames Per Second: " + fps);
vs.FramesPerSecond = fps;
}
@@ -441,4 +442,26 @@ public class VideoInfoHelper
return string.Empty;
return lang;
}
/// <summary>
/// Extracts the supported pixel format from an FFmpeg output line for hardware decoding.
/// </summary>
/// <param name="line">The FFmpeg output line containing video stream information.</param>
/// <returns>The supported pixel format (e.g., "yuv420p" or "p010le"), or an empty string if not found or not supported.</returns>
/// <remarks>
/// Supports "yuv420p" and "p010le" formats. Handles cases where "p010le" has no additional specifiers, defaulting to "yuv420p".
/// Adjust the regular expression or default behavior based on specific hardware and FFmpeg output format.
/// </remarks>
static string GetDecoderPixelFormat(string line)
{
if(line.IndexOf("yuv420p", StringComparison.Ordinal) > 0)
return "yuv420p";
if(line.IndexOf("p010le", StringComparison.Ordinal) > 0)
return "p010le";
if (line.IndexOf("nv12", StringComparison.Ordinal) >= 0)
return "nv12";
if (line.IndexOf("yuv444p", StringComparison.Ordinal) >= 0)
return "yuv444p";
return string.Empty;
}
}