From cf1552910a07c8e6c781c66f0e3541737768c2c5 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Sun, 9 Apr 2023 20:21:03 -0500 Subject: [PATCH] limit hls direct streams to realtime speed (#1237) --- CHANGELOG.md | 3 ++ .../PipelineBuilderBaseTests.cs | 7 ++- .../Pipeline/PipelineBuilderBase.cs | 53 +++++++++++++------ 3 files changed, 44 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f11eae2..c4c250df 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +### Fixed +- Limit `HLS Direct` streams to realtime speed + ## [0.7.7-beta] - 2023-04-07 ### Added - Use `plot` field from Other Video NFO metadata as XMLTV description diff --git a/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs b/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs index fdb57c50..32278acc 100644 --- a/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs +++ b/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using ErsatzTV.FFmpeg.Capabilities; using ErsatzTV.FFmpeg.Encoder; using ErsatzTV.FFmpeg.Format; +using ErsatzTV.FFmpeg.Option; using ErsatzTV.FFmpeg.OutputFormat; using ErsatzTV.FFmpeg.Pipeline; using ErsatzTV.FFmpeg.State; @@ -325,11 +326,13 @@ public class PipelineBuilderBaseTests result.PipelineSteps.Should().HaveCountGreaterThan(0); result.PipelineSteps.Should().Contain(ps => ps is EncoderCopyVideo); result.PipelineSteps.Should().Contain(ps => ps is EncoderCopyAudio); + videoInputFile.InputOptions.Should().Contain(io => io is RealtimeInputOption); string command = PrintCommand(videoInputFile, audioInputFile, None, None, result); + // 0.4.0 reference: "-nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -re -ss 00:14:33.6195516 -i /tmp/whatever.mkv -map 0:0 -map 0:a -c:v copy -flags cgop -sc_threshold 0 -c:a copy -movflags +faststart -muxdelay 0 -muxpreload 0 -metadata service_provider="ErsatzTV" -metadata service_name="ErsatzTV" -t 00:06:39.6934484 -f mpegts -mpegts_flags +initial_discontinuity pipe:1" command.Should().Be( - "-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mpegts -mpegts_flags +initial_discontinuity pipe:1"); + "-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -re -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mpegts -mpegts_flags +initial_discontinuity pipe:1"); } [Test] @@ -407,7 +410,7 @@ public class PipelineBuilderBaseTests string command = PrintCommand(videoInputFile, audioInputFile, None, None, result); command.Should().Be( - "-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -i /tmp/whatever.mkv -map 0:a -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mpegts -mpegts_flags +initial_discontinuity pipe:1"); + "-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -re -i /tmp/whatever.mkv -map 0:a -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mpegts -mpegts_flags +initial_discontinuity pipe:1"); } [Test] diff --git a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs index 0c429bc0..d2171c52 100644 --- a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs +++ b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs @@ -174,22 +174,13 @@ public abstract class PipelineBuilderBase : IPipelineBuilder SetStreamSeek(ffmpegState, videoInputFile, context, pipelineSteps); SetTimeLimit(ffmpegState, pipelineSteps); - FilterChain filterChain = FilterChain.Empty; - - if (desiredState.VideoFormat == VideoFormat.Copy) - { - pipelineSteps.Add(new EncoderCopyVideo()); - } - else - { - filterChain = BuildVideoPipeline( - videoInputFile, - videoStream, - ffmpegState, - desiredState, - context, - pipelineSteps); - } + FilterChain filterChain = BuildVideoPipeline( + videoInputFile, + videoStream, + ffmpegState, + desiredState, + context, + pipelineSteps); if (_audioInputFile.IsNone) { @@ -398,7 +389,10 @@ public abstract class PipelineBuilderBase : IPipelineBuilder ffmpegState = SetAccelState(videoStream, ffmpegState, desiredState, context, pipelineSteps); - Option maybeDecoder = SetDecoder(videoInputFile, videoStream, ffmpegState, context); + // don't use explicit decoder with HLS Direct + Option maybeDecoder = desiredState.VideoFormat == VideoFormat.Copy + ? None + : SetDecoder(videoInputFile, videoStream, ffmpegState, context); SetStillImageInfiniteLoop(videoInputFile, videoStream, ffmpegState); SetRealtimeInput(videoInputFile, desiredState); @@ -473,6 +467,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder private static void SetOutputTsOffset(FFmpegState ffmpegState, FrameState desiredState, ICollection pipelineSteps) { + if (desiredState.VideoFormat == VideoFormat.Copy) + { + return; + } + if (ffmpegState.PtsOffset > 0) { foreach (int videoTrackTimeScale in desiredState.VideoTrackTimeScale) @@ -484,6 +483,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder private static void SetVideoBufferSizeOutput(FrameState desiredState, ICollection pipelineSteps) { + if (desiredState.VideoFormat == VideoFormat.Copy) + { + return; + } + foreach (int desiredBufferSize in desiredState.VideoBufferSize) { pipelineSteps.Add(new VideoBufferSizeOutputOption(desiredBufferSize)); @@ -492,6 +496,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder private static void SetVideoBitrateOutput(FrameState desiredState, ICollection pipelineSteps) { + if (desiredState.VideoFormat == VideoFormat.Copy) + { + return; + } + foreach (int desiredBitrate in desiredState.VideoBitrate) { pipelineSteps.Add(new VideoBitrateOutputOption(desiredBitrate)); @@ -500,6 +509,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder private static void SetVideoTrackTimescaleOutput(FrameState desiredState, ICollection pipelineSteps) { + if (desiredState.VideoFormat == VideoFormat.Copy) + { + return; + } + foreach (int desiredTimeScale in desiredState.VideoTrackTimeScale) { pipelineSteps.Add(new VideoTrackTimescaleOutputOption(desiredTimeScale)); @@ -508,6 +522,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder private static void SetFrameRateOutput(FrameState desiredState, ICollection pipelineSteps) { + if (desiredState.VideoFormat == VideoFormat.Copy) + { + return; + } + foreach (int desiredFrameRate in desiredState.FrameRate) { pipelineSteps.Add(new FrameRateOutputOption(desiredFrameRate));