diff --git a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs index 7e42fe75d..c203e99e2 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs @@ -248,7 +248,7 @@ public class PrepareTroubleshootingPlaybackHandler( inPoint, channelStartTime: DateTimeOffset.Now, TimeSpan.Zero, - None, + Option.None, FileSystemLayout.TranscodeTroubleshootingFolder, _ => { }, cancellationToken); diff --git a/ErsatzTV.FFmpeg/Filter/ResetPtsFilter.cs b/ErsatzTV.FFmpeg/Filter/ResetPtsFilter.cs index dd724b1f0..a6053e119 100644 --- a/ErsatzTV.FFmpeg/Filter/ResetPtsFilter.cs +++ b/ErsatzTV.FFmpeg/Filter/ResetPtsFilter.cs @@ -1,8 +1,8 @@ namespace ErsatzTV.FFmpeg.Filter; -public class ResetPtsFilter : BaseFilter +public class ResetPtsFilter(string fps) : BaseFilter { - public override string Filter => "setpts=PTS-STARTPTS"; + public override string Filter => $"setpts=PTS-STARTPTS,fps={fps}"; public override FrameState NextState(FrameState currentState) => currentState; } diff --git a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs index f4d8a260e..343eb356b 100644 --- a/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs +++ b/ErsatzTV.FFmpeg/Pipeline/QsvPipelineBuilder.cs @@ -1,3 +1,4 @@ +using System.Globalization; using ErsatzTV.FFmpeg.Capabilities; using ErsatzTV.FFmpeg.Decoder; using ErsatzTV.FFmpeg.Decoder.Qsv; @@ -170,8 +171,6 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder currentState = decoder.NextState(currentState); } - videoInputFile.FilterSteps.Add(new ResetPtsFilter()); - // easier to use nv12 for overlay if (context.HasSubtitleOverlay || context.HasWatermark || context.HasGraphicsEngine) { @@ -194,15 +193,27 @@ public class QsvPipelineBuilder : SoftwarePipelineBuilder currentState = SetCrop(videoInputFile, desiredState, currentState); SetStillImageLoop(videoInputFile, videoStream, ffmpegState, desiredState, pipelineSteps); - // need to download for any sort of overlay - if (currentState.FrameDataLocation == FrameDataLocation.Hardware && - (context.HasSubtitleOverlay || context.HasWatermark || context.HasGraphicsEngine)) + // need to download for any sort of overlay (and always for setpts) + if (currentState.FrameDataLocation == FrameDataLocation.Hardware) //&& + //(context.HasSubtitleOverlay || context.HasWatermark || context.HasGraphicsEngine)) { var hardwareDownload = new HardwareDownloadFilter(currentState); currentState = hardwareDownload.NextState(currentState); videoInputFile.FilterSteps.Add(hardwareDownload); } + // use normalized fps, or source fps + string fps = desiredState.FrameRate.Match( + r => r.ToString(NumberFormatInfo.InvariantInfo), + () => videoStream.FrameRate.IfNone("24")); + videoInputFile.FilterSteps.Add(new ResetPtsFilter(fps)); + + // since fps will set frame rate, remove the output option + foreach (var frameRate in pipelineSteps.OfType().HeadOrNone()) + { + pipelineSteps.Remove(frameRate); + } + currentState = SetSubtitle( videoInputFile, subtitleInputFile,