Browse Source

fix nvidia compatibility with ffmpeg 7.2+ (#2108)

* tweak random seed

* fix dotnet install in docker test

* fix nvidia compatibility with ffmpeg 7.2+
pull/2106/head
Jason Dove 1 month ago committed by GitHub
parent
commit
a6fa93d44e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 1
      CHANGELOG.md
  2. 3
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs
  3. 3
      ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutSkipItemsHandler.cs
  4. 10
      ErsatzTV.FFmpeg/Filter/Cuda/ScaleCudaFilter.cs
  5. 18
      ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs
  6. 7
      docker/ffmpeg-tests/Dockerfile

1
CHANGELOG.md

@ -87,6 +87,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -87,6 +87,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- This allows these endpoints to be accessed through port `ETV_STREAMING_PORT` (default `8409`)
- This only matters if you configured `ETV_UI_PORT` to be a different value, which makes UI endpoints inaccessible on the streaming port
- Update Plex movie/other video plot ("summary") during library deep scan
- Fix compatibility with ffmpeg 7.2+ when using NVIDIA accel and 10-bit source content
## [25.2.0] - 2025-06-24
### Added

3
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutCountHandler.cs

@ -28,7 +28,8 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay @@ -28,7 +28,8 @@ public class YamlPlayoutCountHandler(EnumeratorCache enumeratorCache) : YamlPlay
foreach (IMediaCollectionEnumerator enumerator in maybeEnumerator)
{
var random = new Random(context.Playout.Seed + context.InstructionIndex);
int seed = context.Playout.Seed + context.InstructionIndex + context.CurrentTime.DayOfYear;
var random = new Random(seed);
int enumeratorCount = enumerator is PlaylistEnumerator playlistEnumerator
? playlistEnumerator.CountForRandom
: enumerator.Count;

3
ErsatzTV.Core/Scheduling/YamlScheduling/Handlers/YamlPlayoutSkipItemsHandler.cs

@ -27,7 +27,8 @@ public class YamlPlayoutSkipItemsHandler(EnumeratorCache enumeratorCache) : IYam @@ -27,7 +27,8 @@ public class YamlPlayoutSkipItemsHandler(EnumeratorCache enumeratorCache) : IYam
foreach (IMediaCollectionEnumerator enumerator in maybeEnumerator)
{
var random = new Random(context.Playout.Seed + context.InstructionIndex);
int seed = context.Playout.Seed + context.InstructionIndex + context.CurrentTime.DayOfYear;
var random = new Random(seed);
int enumeratorCount = enumerator is PlaylistEnumerator playlistEnumerator
? playlistEnumerator.CountForRandom
: enumerator.Count;

10
ErsatzTV.FFmpeg/Filter/Cuda/ScaleCudaFilter.cs

@ -7,6 +7,7 @@ public class ScaleCudaFilter : BaseFilter @@ -7,6 +7,7 @@ public class ScaleCudaFilter : BaseFilter
private readonly Option<FrameSize> _croppedSize;
private readonly FrameState _currentState;
private readonly bool _isAnamorphicEdgeCase;
private readonly bool _passthrough;
private readonly FrameSize _paddedSize;
private readonly FrameSize _scaledSize;
@ -15,13 +16,15 @@ public class ScaleCudaFilter : BaseFilter @@ -15,13 +16,15 @@ public class ScaleCudaFilter : BaseFilter
FrameSize scaledSize,
FrameSize paddedSize,
Option<FrameSize> croppedSize,
bool isAnamorphicEdgeCase)
bool isAnamorphicEdgeCase,
bool passthrough)
{
_currentState = currentState;
_scaledSize = scaledSize;
_paddedSize = paddedSize;
_croppedSize = croppedSize;
_isAnamorphicEdgeCase = isAnamorphicEdgeCase;
_passthrough = passthrough;
}
public bool IsFormatOnly => _currentState.ScaledSize == _scaledSize;
@ -33,10 +36,11 @@ public class ScaleCudaFilter : BaseFilter @@ -33,10 +36,11 @@ public class ScaleCudaFilter : BaseFilter
string scale = string.Empty;
if (_currentState.ScaledSize == _scaledSize)
{
// don't need scaling, but still need pixel format
foreach (IPixelFormat pixelFormat in _currentState.PixelFormat)
{
// don't need scaling, but still need pixel format
scale = $"scale_cuda=format={pixelFormat.FFmpegName}";
string passthrough = _passthrough ? ":passthrough=1" : string.Empty;
scale = $"scale_cuda=format={pixelFormat.FFmpegName}{passthrough}";
}
}
else

18
ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs

@ -163,6 +163,20 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -163,6 +163,20 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
foreach (IDecoder decoder in maybeDecoder)
{
currentState = decoder.NextState(currentState);
// ffmpeg 7.2+ uses p016 internally for cuda, so convert to p010 for compatibility until min ver is 7.2
if (decoder is DecoderImplicitCuda && videoStream.PixelFormat.Map(pf => pf.BitDepth).IfNone(8) == 10)
{
var filter = new ScaleCudaFilter(
currentState with { PixelFormat = new PixelFormatP010() },
videoStream.FrameSize,
videoStream.FrameSize,
Option<FrameSize>.None,
false,
passthrough: true);
currentState = filter.NextState(currentState);
videoInputFile.FilterSteps.Add(filter);
}
}
// if (context.HasSubtitleOverlay || context.HasWatermark)
@ -214,6 +228,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -214,6 +228,7 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
currentState.ScaledSize,
currentState.PaddedSize,
Option<FrameSize>.None,
false,
false);
currentState = filter.NextState(currentState);
videoInputFile.FilterSteps.Add(filter);
@ -693,7 +708,8 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -693,7 +708,8 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
desiredState.ScaledSize,
desiredState.PaddedSize,
desiredState.CroppedSize,
VideoStream.IsAnamorphicEdgeCase);
VideoStream.IsAnamorphicEdgeCase,
false);
}
if (!string.IsNullOrWhiteSpace(scaleStep.Filter))

7
docker/ffmpeg-tests/Dockerfile

@ -1,5 +1,10 @@ @@ -1,5 +1,10 @@
FROM jasongdove/ersatztv-ffmpeg:7.1.1
RUN apt-get update && apt-get install -y ca-certificates gnupg dotnet9 dotnet-sdk-9.0 aspnetcore-runtime-9.0 mkvtoolnix
RUN apt-get update && apt-get install -y ca-certificates gnupg mkvtoolnix && \
curl -L https://dot.net/v1/dotnet-install.sh -o dotnet-install.sh && \
chmod +x ./dotnet-install.sh && \
./dotnet-install.sh --channel 9.0
ENV DOTNET_ROOT="/root/.dotnet"
ENV PATH="$PATH:$DOTNET_ROOT:$DOTNET_ROOT/tools"
WORKDIR /source
# copy csproj and restore as distinct layers

Loading…
Cancel
Save