Browse Source

windows nvidia h264 workaround (#1487)

* work around bad h264_cuvid behavior on windows with ffmpeg snapshot

* use latest ffmpeg build on windows

* nvdec => cuda
pull/1489/head
Jason Dove 2 years ago committed by GitHub
parent
commit
f5060522aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      .github/workflows/artifacts.yml
  2. 13
      ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderImplicitCuda.cs
  3. 2
      ErsatzTV.FFmpeg/Encoder/Nvenc/EncoderHevcNvenc.cs
  4. 23
      ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs
  5. 10
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs
  6. 2
      ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs
  7. 2
      ErsatzTV.sln.DotSettings

2
.github/workflows/artifacts.yml

@ -208,7 +208,7 @@ jobs: @@ -208,7 +208,7 @@ jobs:
id: downloadffmpeg
name: Download ffmpeg
with:
url: "https://github.com/GyanD/codexffmpeg/releases/download/2023-10-04-git-9078dc0c52/ffmpeg-2023-10-04-git-9078dc0c52-full_build.7z"
url: "https://github.com/GyanD/codexffmpeg/releases/download/2023-10-16-git-5ddab49d48/ffmpeg-2023-10-16-git-5ddab49d48-full_build.7z"
target: ffmpeg/
- name: Build

13
ErsatzTV.FFmpeg/Decoder/Cuvid/DecoderImplicitCuda.cs

@ -0,0 +1,13 @@ @@ -0,0 +1,13 @@
namespace ErsatzTV.FFmpeg.Decoder.Cuvid;
public class DecoderImplicitCuda : DecoderBase
{
protected override FrameDataLocation OutputFrameDataLocation => FrameDataLocation.Hardware;
public override string Name => string.Empty;
public override IList<string> InputOptions(InputFile inputFile) =>
new List<string>
{
"-hwaccel_output_format",
"cuda"
};
}

2
ErsatzTV.FFmpeg/Encoder/Nvenc/EncoderHevcNvenc.cs

@ -19,7 +19,7 @@ public class EncoderHevcNvenc : EncoderBase @@ -19,7 +19,7 @@ public class EncoderHevcNvenc : EncoderBase
public override StreamKind Kind => StreamKind.Video;
public override IList<string> OutputOptions =>
new[] { "-c:v", "hevc_nvenc", "-b_ref_mode", _bFrames ? "1" : "0" };
new[] { "-c:v", "hevc_nvenc", "-tag:v", "hvc1", "-b_ref_mode", _bFrames ? "1" : "0" };
public override FrameState NextState(FrameState currentState) => currentState with
{

23
ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs

@ -51,11 +51,6 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -51,11 +51,6 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
_logger = logger;
}
protected override bool IsNvidiaOnWindows(FFmpegState ffmpegState) =>
_runtimeInfo.IsOSPlatform(OSPlatform.Windows) &&
(ffmpegState.EncoderHardwareAccelerationMode is HardwareAccelerationMode.Nvenc ||
ffmpegState.DecoderHardwareAccelerationMode is HardwareAccelerationMode.Nvenc);
protected override FFmpegState SetAccelState(
VideoStream videoStream,
FFmpegState ffmpegState,
@ -101,6 +96,13 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -101,6 +96,13 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
FFmpegState ffmpegState,
PipelineContext context)
{
if (NeedsCudaWorkaround(ffmpegState, videoStream.Codec))
{
var cuda = new DecoderImplicitCuda();
videoInputFile.AddOption(cuda);
return cuda;
}
Option<IDecoder> maybeDecoder = (ffmpegState.DecoderHardwareAccelerationMode, videoStream.Codec) switch
{
(HardwareAccelerationMode.Nvenc, VideoFormat.Hevc) => new DecoderHevcCuvid(HardwareAccelerationMode.Nvenc),
@ -645,4 +647,15 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -645,4 +647,15 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
return currentState;
}
// the combination of:
// - windows
// - ffmpeg 6.1 snapshot (where readrate_initial_burst option is present)
// - h264_cuvid
// appears to cause jitter, so use implicit cuda decoder in that specific case
private bool NeedsCudaWorkaround(FFmpegState ffmpegState, string videoFormat) =>
_runtimeInfo.IsOSPlatform(OSPlatform.Windows)
&& _ffmpegCapabilities.HasOption(FFmpegKnownOption.ReadrateInitialBurst)
&& ffmpegState.DecoderHardwareAccelerationMode is HardwareAccelerationMode.Nvenc
&& videoFormat == VideoFormat.H264;
}

10
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

@ -1,5 +1,4 @@ @@ -1,5 +1,4 @@
using System.Diagnostics;
using System.Runtime.InteropServices;
using ErsatzTV.FFmpeg.Capabilities;
using ErsatzTV.FFmpeg.Decoder;
using ErsatzTV.FFmpeg.Encoder;
@ -12,7 +11,6 @@ using ErsatzTV.FFmpeg.OutputFormat; @@ -12,7 +11,6 @@ using ErsatzTV.FFmpeg.OutputFormat;
using ErsatzTV.FFmpeg.OutputOption;
using ErsatzTV.FFmpeg.OutputOption.Metadata;
using ErsatzTV.FFmpeg.Protocol;
using ErsatzTV.FFmpeg.Runtime;
using Microsoft.Extensions.Logging;
namespace ErsatzTV.FFmpeg.Pipeline;
@ -404,7 +402,6 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -404,7 +402,6 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
}
protected abstract bool IsIntelVaapiOrQsv(FFmpegState ffmpegState);
protected abstract bool IsNvidiaOnWindows(FFmpegState ffmpegState);
protected abstract FFmpegState SetAccelState(
VideoStream videoStream,
@ -643,13 +640,6 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -643,13 +640,6 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
};
}
// TODO: when ffmpeg/nvenc stops being weird on windows, remove this workaround
if (IsNvidiaOnWindows(ffmpegState))
{
// disable initial burst
initialBurst = 0;
}
_audioInputFile.Iter(a => a.AddOption(new ReadrateInputOption(_ffmpegCapabilities, initialBurst, _logger)));
videoInputFile.AddOption(new ReadrateInputOption(_ffmpegCapabilities, initialBurst, _logger));
}

2
ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs

@ -37,8 +37,6 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase @@ -37,8 +37,6 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase
protected override bool IsIntelVaapiOrQsv(FFmpegState ffmpegState) => false;
protected override bool IsNvidiaOnWindows(FFmpegState ffmpegState) => false;
protected override FFmpegState SetAccelState(
VideoStream videoStream,
FFmpegState ffmpegState,

2
ErsatzTV.sln.DotSettings

@ -53,11 +53,13 @@ @@ -53,11 +53,13 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=muxdelay/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=muxpreload/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=nostats/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=nvdec/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pixfmt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=playout/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Playouts/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=probesize/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Radeon/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=readrate/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Segmenter/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=setsar/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=showtitle/@EntryIndexedValue">True</s:Boolean>

Loading…
Cancel
Save