Browse Source

improve hls throttle (#1434)

* throttle using ffmpeg option

* update ffmpeg version
pull/1435/head
Jason Dove 2 years ago committed by GitHub
parent
commit
b16215fcd6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      ErsatzTV.Application/Streaming/HlsSessionWorker.cs
  2. 15
      ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs
  3. 14
      ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs
  4. 29
      ErsatzTV.FFmpeg/InputOption/ReadrateInputOption.cs
  5. 13
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs
  6. 4
      ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs

2
ErsatzTV.Application/Streaming/HlsSessionWorker.cs

@ -19,8 +19,6 @@ namespace ErsatzTV.Application.Streaming; @@ -19,8 +19,6 @@ namespace ErsatzTV.Application.Streaming;
public class HlsSessionWorker : IHlsSessionWorker
{
public static readonly TimeSpan WorkAheadDuration = TimeSpan.FromMinutes(3);
private static readonly SemaphoreSlim Slim = new(1, 1);
private static int _workAheadCount;
private readonly IMediator _mediator;

15
ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs

@ -178,19 +178,6 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -178,19 +178,6 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
TimeSpan outPoint = playoutItemWithPath.PlayoutItem.OutPoint;
DateTimeOffset effectiveNow = request.StartAtZero ? start : now;
TimeSpan duration = finish - effectiveNow;
var isComplete = true;
// _logger.LogDebug("PRE Start: {Start}, Finish {Finish}", start, finish);
// _logger.LogDebug("PRE in: {In}, out: {Out}", inPoint, outPoint);
if (!request.HlsRealtime && duration > HlsSessionWorker.WorkAheadDuration)
{
finish = effectiveNow + HlsSessionWorker.WorkAheadDuration;
outPoint = finish - start + inPoint;
isComplete = false;
// _logger.LogDebug("POST Start: {Start}, Finish {Finish}", start, finish);
// _logger.LogDebug("POST in: {In}, out: {Out}", inPoint, outPoint);
}
Command process = await _ffmpegProcessService.ForPlayoutItem(
ffmpegPath,
@ -223,7 +210,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< @@ -223,7 +210,7 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
playoutItemWithPath.PlayoutItem.DisableWatermarks,
_ => { });
var result = new PlayoutItemProcessModel(process, duration, finish, isComplete);
var result = new PlayoutItemProcessModel(process, duration, finish, true);
return Right<BaseError, PlayoutItemProcessModel>(result);
}

14
ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs

@ -106,7 +106,7 @@ public class PipelineBuilderBaseTests @@ -106,7 +106,7 @@ public class PipelineBuilderBaseTests
string command = PrintCommand(videoInputFile, audioInputFile, None, None, result);
command.Should().Be(
"-threads 1 -nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -re -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -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");
"-threads 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 -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -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]
@ -194,7 +194,7 @@ public class PipelineBuilderBaseTests @@ -194,7 +194,7 @@ public class PipelineBuilderBaseTests
string command = PrintCommand(videoInputFile, audioInputFile, None, None, result);
command.Should().Be(
"-threads 1 -nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -ss 00:00:01 -c:v h264 -re -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -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");
"-threads 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 -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -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]
@ -220,7 +220,7 @@ public class PipelineBuilderBaseTests @@ -220,7 +220,7 @@ public class PipelineBuilderBaseTests
string command = PrintCommand(None, None, None, concatInputFile, result);
command.Should().Be(
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -f concat -safe 0 -protocol_whitelist file,http,tcp,https,tcp,tls -probesize 32 -re -stream_loop -1 -i http://localhost:8080/ffmpeg/concat/1 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -c copy -map_metadata -1 -metadata service_provider=\"ErsatzTV\" -metadata service_name=\"Some Channel\" -f mpegts -mpegts_flags +initial_discontinuity pipe:1");
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -f concat -safe 0 -protocol_whitelist file,http,tcp,https,tcp,tls -probesize 32 -readrate 1.0 -stream_loop -1 -i http://localhost:8080/ffmpeg/concat/1 -muxdelay 0 -muxpreload 0 -movflags +faststart -flags cgop -sc_threshold 0 -c copy -map_metadata -1 -metadata service_provider=\"ErsatzTV\" -metadata service_name=\"Some Channel\" -f mpegts -mpegts_flags +initial_discontinuity pipe:1");
}
[Test]
@ -248,7 +248,7 @@ public class PipelineBuilderBaseTests @@ -248,7 +248,7 @@ public class PipelineBuilderBaseTests
string command = PrintCommand(None, None, None, concatInputFile, result);
command.Should().Be(
"-nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -re -i http://localhost:8080/iptv/channel/1.m3u8?mode=segmenter -map 0 -c copy -metadata service_provider=\"ErsatzTV\" -metadata service_name=\"Some Channel\" -f mpegts pipe:1");
"-nostdin -threads 1 -hide_banner -loglevel error -nostats -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i http://localhost:8080/iptv/channel/1.m3u8?mode=segmenter -map 0 -c copy -metadata service_provider=\"ErsatzTV\" -metadata service_name=\"Some Channel\" -f mpegts pipe:1");
}
[Test]
@ -334,13 +334,13 @@ public class PipelineBuilderBaseTests @@ -334,13 +334,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);
videoInputFile.InputOptions.Should().Contain(io => io is ReadrateInputOption);
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 -re -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mp4 pipe:1");
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i /tmp/whatever.mkv -map 0:1 -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mp4 pipe:1");
}
[Test]
@ -420,7 +420,7 @@ public class PipelineBuilderBaseTests @@ -420,7 +420,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 -re -i /tmp/whatever.mkv -map 0:a -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mp4 pipe:1");
"-nostdin -hide_banner -nostats -loglevel error -fflags +genpts+discardcorrupt+igndts -readrate 1.0 -i /tmp/whatever.mkv -map 0:a -map 0:0 -muxdelay 0 -muxpreload 0 -movflags +faststart+frag_keyframe+separate_moof+omit_tfhd_offset+empty_moov+delay_moov -flags cgop -sc_threshold 0 -c:v copy -c:a copy -f mp4 pipe:1");
}
[Test]

29
ErsatzTV.FFmpeg/InputOption/RealtimeInputOption.cs → ErsatzTV.FFmpeg/InputOption/ReadrateInputOption.cs

@ -1,14 +1,37 @@ @@ -1,14 +1,37 @@
using ErsatzTV.FFmpeg.Environment;
using System.Globalization;
using ErsatzTV.FFmpeg.Environment;
namespace ErsatzTV.FFmpeg.InputOption;
public class RealtimeInputOption : IInputOption
public class ReadrateInputOption : IInputOption
{
private readonly int _initialBurstSeconds;
public ReadrateInputOption(int initialBurstSeconds = 0)
{
_initialBurstSeconds = initialBurstSeconds;
}
public IList<EnvironmentVariable> EnvironmentVariables => Array.Empty<EnvironmentVariable>();
public IList<string> GlobalOptions => Array.Empty<string>();
public IList<string> InputOptions(InputFile inputFile) => new List<string> { "-re" };
public IList<string> InputOptions(InputFile inputFile)
{
var result = new List<string> { "-readrate", "1.0" };
if (_initialBurstSeconds > 0)
{
result.AddRange(
new[]
{
"-readrate_initial_burst",
_initialBurstSeconds.ToString(CultureInfo.InvariantCulture)
});
}
return result;
}
public IList<string> FilterOptions => Array.Empty<string>();
public IList<string> OutputOptions => Array.Empty<string>();

13
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

@ -84,7 +84,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -84,7 +84,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
};
concatInputFile.AddOption(new ConcatInputFormat());
concatInputFile.AddOption(new RealtimeInputOption());
concatInputFile.AddOption(new ReadrateInputOption());
concatInputFile.AddOption(new InfiniteLoopInputOption(HardwareAccelerationMode.None));
foreach (int threadCount in ffmpegState.ThreadCount)
@ -130,7 +130,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -130,7 +130,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
new EncoderCopyAll()
};
concatInputFile.AddOption(new RealtimeInputOption());
concatInputFile.AddOption(new ReadrateInputOption());
SetMetadataServiceProvider(ffmpegState, pipelineSteps);
SetMetadataServiceName(ffmpegState, pipelineSteps);
@ -598,11 +598,14 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -598,11 +598,14 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
private void SetRealtimeInput(VideoInputFile videoInputFile, FrameState desiredState)
{
if (desiredState.Realtime)
var initialBurst = 0;
if (!desiredState.Realtime)
{
_audioInputFile.Iter(a => a.AddOption(new RealtimeInputOption()));
videoInputFile.AddOption(new RealtimeInputOption());
initialBurst = 180;
}
_audioInputFile.Iter(a => a.AddOption(new ReadrateInputOption(initialBurst)));
videoInputFile.AddOption(new ReadrateInputOption(initialBurst));
}
private static void SetStillImageInfiniteLoop(

4
ErsatzTV.Infrastructure/Health/Checks/FFmpegVersionHealthCheck.cs

@ -8,8 +8,8 @@ namespace ErsatzTV.Infrastructure.Health.Checks; @@ -8,8 +8,8 @@ namespace ErsatzTV.Infrastructure.Health.Checks;
public class FFmpegVersionHealthCheck : BaseHealthCheck, IFFmpegVersionHealthCheck
{
private const string BundledVersion = "6.0";
private const string BundledVersionVaapi = "6.0";
private const string BundledVersion = "N-112071-g00a837c70c";
private const string BundledVersionVaapi = "N-112071-g00a837c70c";
private readonly IConfigElementRepository _configElementRepository;
public FFmpegVersionHealthCheck(IConfigElementRepository configElementRepository) =>

Loading…
Cancel
Save