Browse Source

qsv and pts fixes (#2184)

* try to fix qsv freezing

* update changelog

* fix unit tests
pull/2186/head
Jason Dove 4 weeks ago committed by GitHub
parent
commit
89b495dc90
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      CHANGELOG.md
  2. 5
      ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs
  3. 4
      ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs
  4. 8
      ErsatzTV.FFmpeg/Filter/AudioSetPtsFilter.cs
  5. 8
      ErsatzTV.FFmpeg/Filter/VideoSetPtsFilter.cs
  6. 8
      ErsatzTV.FFmpeg/FilterChain.cs
  7. 2
      ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs
  8. 16
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs
  9. 2
      ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs
  10. 6
      ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs
  11. 2
      ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs

2
CHANGELOG.md

@ -159,6 +159,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -159,6 +159,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
- Fix block playout EPG generation to use `XMLTV Time Zone` setting
- Fix adding "official" Trakt lists
- Fix searching for `collection` names with spaces or other special characters, e.g. `collection:"Movies - Action"`
- Fix QSV transcoding errors when scaling
- Fix QSV frame freezing in browser
## [25.2.0] - 2025-06-24
### Added

5
ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs

@ -101,7 +101,10 @@ public class PrepareTroubleshootingPlaybackHandler( @@ -101,7 +101,10 @@ public class PrepareTroubleshootingPlaybackHandler(
if (!hlsRealtime && !request.StartFromBeginning)
{
inPoint = TimeSpan.FromSeconds(version.Duration.TotalSeconds / 2.0);
duration = TimeSpan.FromSeconds(duration.TotalSeconds / 2.0);
if (inPoint.TotalSeconds < 30)
{
duration = inPoint;
}
outPoint = inPoint + duration;
}

4
ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs

@ -116,7 +116,7 @@ public class PipelineBuilderBaseTests @@ -116,7 +116,7 @@ public class PipelineBuilderBaseTests
string command = PrintCommand(videoInputFile, audioInputFile, None, None, result);
command.ShouldBe(
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -readrate 1.0 -i /tmp/whatever.mkv -filter_complex [0:1]aresample=async=1:first_pts=0[a] -map 0:0 -map [a] -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -bf 0 -sc_threshold 0 -video_track_timescale 90000 -b:v 2000k -maxrate:v 2000k -bufsize:v 4000k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -c:a aac -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -f mpegts -mpegts_flags +initial_discontinuity pipe:1");
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -readrate 1.0 -i /tmp/whatever.mkv -filter_complex [0:1]aresample=async=1:first_pts=0,asetpts=PTS-STARTPTS[a];[0:0]setpts=PTS-STARTPTS[vpf] -map [vpf] -map [a] -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -bf 0 -sc_threshold 0 -video_track_timescale 90000 -b:v 2000k -maxrate:v 2000k -bufsize:v 4000k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -c:a aac -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -f mpegts -mpegts_flags +initial_discontinuity pipe:1");
}
[Test]
@ -213,7 +213,7 @@ public class PipelineBuilderBaseTests @@ -213,7 +213,7 @@ public class PipelineBuilderBaseTests
string command = PrintCommand(videoInputFile, audioInputFile, None, None, result);
command.ShouldBe(
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -readrate 1.0 -i /tmp/whatever.mkv -filter_complex [0:1]aresample=async=1:first_pts=0[a] -map 0:0 -map [a] -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -bf 0 -sc_threshold 0 -video_track_timescale 90000 -b:v 2000k -maxrate:v 2000k -bufsize:v 4000k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -c:a aac -ac 6 -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -f mpegts -mpegts_flags +initial_discontinuity pipe:1");
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -readrate 1.0 -i /tmp/whatever.mkv -filter_complex [0:1]aresample=async=1:first_pts=0,asetpts=PTS-STARTPTS[a];[0:0]setpts=PTS-STARTPTS[vpf] -map [vpf] -map [a] -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -bf 0 -sc_threshold 0 -video_track_timescale 90000 -b:v 2000k -maxrate:v 2000k -bufsize:v 4000k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -c:a aac -ac 6 -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -f mpegts -mpegts_flags +initial_discontinuity pipe:1");
}
[Test]

8
ErsatzTV.FFmpeg/Filter/AudioSetPtsFilter.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.FFmpeg.Filter;
public class AudioSetPtsFilter : BaseFilter
{
public override string Filter => "asetpts=PTS-STARTPTS";
public override FrameState NextState(FrameState currentState) => currentState;
}

8
ErsatzTV.FFmpeg/Filter/VideoSetPtsFilter.cs

@ -0,0 +1,8 @@ @@ -0,0 +1,8 @@
namespace ErsatzTV.FFmpeg.Filter;
public class VideoSetPtsFilter : BaseFilter
{
public override string Filter => "setpts=PTS-STARTPTS";
public override FrameState NextState(FrameState currentState) => currentState;
}

8
ErsatzTV.FFmpeg/FilterChain.cs

@ -8,11 +8,5 @@ public record FilterChain( @@ -8,11 +8,5 @@ public record FilterChain(
List<IPipelineFilterStep> SubtitleOverlayFilterSteps,
List<IPipelineFilterStep> PixelFormatFilterSteps)
{
public static readonly FilterChain Empty = new(
new List<IPipelineFilterStep>(),
new List<IPipelineFilterStep>(),
new List<IPipelineFilterStep>(),
new List<IPipelineFilterStep>(),
new List<IPipelineFilterStep>(),
new List<IPipelineFilterStep>());
public static readonly FilterChain Empty = new([], [], [], [], [], []);
}

2
ErsatzTV.FFmpeg/Pipeline/NvidiaPipelineBuilder.cs

@ -306,6 +306,8 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder @@ -306,6 +306,8 @@ public class NvidiaPipelineBuilder : SoftwarePipelineBuilder
context,
pipelineSteps);
pixelFormatFilterSteps.Add(new VideoSetPtsFilter());
return new FilterChain(
videoInputFile.FilterSteps,
watermarkInputFile.Map(wm => wm.FilterSteps).IfNone(new List<IPipelineFilterStep>()),

16
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

@ -327,6 +327,9 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -327,6 +327,9 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
{
foreach (string segmentTemplate in ffmpegState.HlsSegmentTemplate)
{
//bool oneSecondGop = ffmpegState.EncoderHardwareAccelerationMode is HardwareAccelerationMode.Qsv;
var oneSecondGop = false;
pipelineSteps.Add(
new OutputFormatHls(
desiredState,
@ -334,7 +337,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -334,7 +337,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
segmentTemplate,
playlistPath,
ffmpegState.PtsOffset == 0,
ffmpegState.EncoderHardwareAccelerationMode is HardwareAccelerationMode.Qsv,
oneSecondGop,
ffmpegState.IsTroubleshooting));
}
}
@ -412,16 +415,21 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -412,16 +415,21 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
SetAudioPad(audioInputFile, pipelineSteps);
}
private void SetAudioPad(AudioInputFile audioInputFile, List<IPipelineStep> pipelineSteps)
private static void SetAudioPad(AudioInputFile audioInputFile, List<IPipelineStep> pipelineSteps)
{
if (pipelineSteps.All(ps => ps is not EncoderCopyAudio))
{
_audioInputFile.Iter(f => f.FilterSteps.Add(new AudioFirstPtsFilter(0)));
audioInputFile.FilterSteps.Add(new AudioFirstPtsFilter(0));
}
foreach (TimeSpan _ in audioInputFile.DesiredState.AudioDuration)
{
_audioInputFile.Iter(f => f.FilterSteps.Add(new AudioPadFilter()));
audioInputFile.FilterSteps.Add(new AudioPadFilter());
}
if (pipelineSteps.All(ps => ps is not EncoderCopyAudio))
{
audioInputFile.FilterSteps.Add(new AudioSetPtsFilter());
}
}

2
ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs

@ -248,6 +248,8 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder @@ -248,6 +248,8 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder
context,
pipelineSteps);
pixelFormatFilterSteps.Add(new VideoSetPtsFilter());
return new FilterChain(
videoInputFile.FilterSteps,
watermarkInputFile.Map(wm => wm.FilterSteps).IfNone(new List<IPipelineFilterStep>()),

6
ErsatzTV.FFmpeg/Pipeline/SoftwarePipelineBuilder.cs

@ -148,10 +148,12 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase @@ -148,10 +148,12 @@ public class SoftwarePipelineBuilder : PipelineBuilderBase
currentState,
pipelineSteps);
pixelFormatFilterSteps.Add(new VideoSetPtsFilter());
return new FilterChain(
videoInputFile.FilterSteps,
watermarkInputFile.Map(wm => wm.FilterSteps).IfNone(new List<IPipelineFilterStep>()),
subtitleInputFile.Map(st => st.FilterSteps).IfNone(new List<IPipelineFilterStep>()),
watermarkInputFile.Map(wm => wm.FilterSteps).IfNone([]),
subtitleInputFile.Map(st => st.FilterSteps).IfNone([]),
watermarkOverlayFilterSteps,
subtitleOverlayFilterSteps,
pixelFormatFilterSteps);

2
ErsatzTV.FFmpeg/Pipeline/VaapiPipelineBuilder.cs

@ -260,6 +260,8 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder @@ -260,6 +260,8 @@ public class VaapiPipelineBuilder : SoftwarePipelineBuilder
currentState,
pipelineSteps);
pixelFormatFilterSteps.Add(new VideoSetPtsFilter());
return new FilterChain(
videoInputFile.FilterSteps,
watermarkInputFile.Map(wm => wm.FilterSteps).IfNone(new List<IPipelineFilterStep>()),

Loading…
Cancel
Save