Browse Source

always use a single ffmpeg thread with realtime (#580)

pull/581/head
Jason Dove 5 years ago committed by GitHub
parent
commit
03946b13ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      CHANGELOG.md
  2. 130
      ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs
  3. 2
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs
  4. 14
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs
  5. 9
      ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs
  6. 11
      ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

4
CHANGELOG.md

@ -4,6 +4,10 @@ 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/). The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
## [Unreleased] ## [Unreleased]
### Fixed
- Fix issue preventing some versions of ffmpeg (usually 4.4.x) from streaming MPEG-TS (Legacy) channels at all
- The issue appears to be caused by using a thread count other than `1`
- Thread count is now forced to `1` for all streaming modes other than HLS Segmenter
## [0.3.7-alpha] - 2022-01-17 ## [0.3.7-alpha] - 2022-01-17
### Fixed ### Fixed

130
ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs

@ -18,8 +18,10 @@ namespace ErsatzTV.Core.Tests.FFmpeg
public CalculateSettings() => _calculator = new FFmpegPlaybackSettingsCalculator(); public CalculateSettings() => _calculator = new FFmpegPlaybackSettingsCalculator();
[Test] [Test]
public void Should_UseSpecifiedThreadCount_ForTransportStream() public void Should_Not_UseSpecifiedThreadCount_ForTransportStream()
{ {
// MPEG-TS requires realtime output which is hardcoded to a single thread
FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 }; FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 };
FFmpegPlaybackSettings actual = _calculator.CalculateSettings( FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
@ -31,18 +33,19 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ThreadCount.Should().Be(7); actual.ThreadCount.Should().Be(1);
} }
[Test] [Test]
public void Should_UseSpecifiedThreadCount_ForHttpLiveStreaming() public void Should_UseSpecifiedThreadCount_ForHttpLiveStreamingSegmenter()
{ {
FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 }; FFmpegProfile ffmpegProfile = TestProfile() with { ThreadCount = 7 };
FFmpegPlaybackSettings actual = _calculator.CalculateSettings( FFmpegPlaybackSettings actual = _calculator.CalculateSettings(
StreamingMode.HttpLiveStreamingDirect, StreamingMode.HttpLiveStreamingSegmenter,
ffmpegProfile, ffmpegProfile,
new MediaVersion(), new MediaVersion(),
new MediaStream(), new MediaStream(),
@ -50,7 +53,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ThreadCount.Should().Be(7); actual.ThreadCount.Should().Be(7);
} }
@ -69,7 +73,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
string[] expected = { "+genpts", "+discardcorrupt", "+igndts" }; string[] expected = { "+genpts", "+discardcorrupt", "+igndts" };
actual.FormatFlags.Count.Should().Be(expected.Length); actual.FormatFlags.Count.Should().Be(expected.Length);
@ -90,7 +95,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
string[] expected = { "+genpts", "+discardcorrupt", "+igndts" }; string[] expected = { "+genpts", "+discardcorrupt", "+igndts" };
actual.FormatFlags.Count.Should().Be(expected.Length); actual.FormatFlags.Count.Should().Be(expected.Length);
@ -111,7 +117,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.RealtimeOutput.Should().BeTrue(); actual.RealtimeOutput.Should().BeTrue();
} }
@ -130,7 +137,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.RealtimeOutput.Should().BeTrue(); actual.RealtimeOutput.Should().BeTrue();
} }
@ -151,7 +159,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
now, now,
now.AddMinutes(5), now.AddMinutes(5),
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.StreamSeek.IsSome.Should().BeTrue(); actual.StreamSeek.IsSome.Should().BeTrue();
actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5)); actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5));
@ -173,7 +182,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
now, now,
now.AddMinutes(5), now.AddMinutes(5),
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.StreamSeek.IsSome.Should().BeTrue(); actual.StreamSeek.IsSome.Should().BeTrue();
actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5)); actual.StreamSeek.IfNone(TimeSpan.Zero).Should().Be(TimeSpan.FromMinutes(5));
@ -193,7 +203,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
} }
@ -219,7 +230,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
} }
@ -245,7 +257,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
} }
@ -271,7 +284,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -299,7 +313,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeTrue(); actual.PadToDesiredResolution.Should().BeTrue();
@ -326,7 +341,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
IDisplaySize scaledSize = actual.ScaledSize.IfNone(new MediaVersion { Width = 0, Height = 0 }); IDisplaySize scaledSize = actual.ScaledSize.IfNone(new MediaVersion { Width = 0, Height = 0 });
scaledSize.Width.Should().Be(1280); scaledSize.Width.Should().Be(1280);
@ -356,7 +372,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -384,7 +401,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -413,7 +431,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeTrue(); actual.PadToDesiredResolution.Should().BeTrue();
@ -445,7 +464,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -477,7 +497,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -508,7 +529,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -540,7 +562,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -574,7 +597,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -606,7 +630,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeTrue(); actual.PadToDesiredResolution.Should().BeTrue();
@ -637,7 +662,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -667,7 +693,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeTrue(); actual.PadToDesiredResolution.Should().BeTrue();
@ -699,7 +726,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.ScaledSize.IsNone.Should().BeTrue(); actual.ScaledSize.IsNone.Should().BeTrue();
actual.PadToDesiredResolution.Should().BeFalse(); actual.PadToDesiredResolution.Should().BeFalse();
@ -727,7 +755,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioCodec.Should().Be("aac"); actual.AudioCodec.Should().Be("aac");
} }
@ -752,7 +781,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioCodec.Should().Be("copy"); actual.AudioCodec.Should().Be("copy");
} }
@ -778,7 +808,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioCodec.Should().Be("aac"); actual.AudioCodec.Should().Be("aac");
} }
@ -804,7 +835,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioCodec.Should().Be("copy"); actual.AudioCodec.Should().Be("copy");
} }
@ -831,7 +863,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioBitrate.IfNone(0).Should().Be(2424); actual.AudioBitrate.IfNone(0).Should().Be(2424);
} }
@ -858,7 +891,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioBufferSize.IfNone(0).Should().Be(2424); actual.AudioBufferSize.IfNone(0).Should().Be(2424);
} }
@ -885,7 +919,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioChannels.IfNone(0).Should().Be(6); actual.AudioChannels.IfNone(0).Should().Be(6);
} }
@ -912,7 +947,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioSampleRate.IfNone(0).Should().Be(48); actual.AudioSampleRate.IfNone(0).Should().Be(48);
} }
@ -938,7 +974,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioChannels.IfNone(0).Should().Be(6); actual.AudioChannels.IfNone(0).Should().Be(6);
} }
@ -964,7 +1001,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.AudioSampleRate.IfNone(0).Should().Be(48); actual.AudioSampleRate.IfNone(0).Should().Be(48);
} }
@ -991,7 +1029,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.FromMinutes(2)); TimeSpan.FromMinutes(2),
false);
actual.AudioDuration.IfNone(TimeSpan.MinValue).Should().Be(TimeSpan.FromMinutes(2)); actual.AudioDuration.IfNone(TimeSpan.MinValue).Should().Be(TimeSpan.FromMinutes(2));
} }
@ -1017,7 +1056,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.NormalizeLoudness.Should().BeTrue(); actual.NormalizeLoudness.Should().BeTrue();
} }
@ -1043,7 +1083,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.NormalizeLoudness.Should().BeFalse(); actual.NormalizeLoudness.Should().BeFalse();
} }
@ -1071,7 +1112,8 @@ namespace ErsatzTV.Core.Tests.FFmpeg
DateTimeOffset.Now, DateTimeOffset.Now,
DateTimeOffset.Now, DateTimeOffset.Now,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
actual.HardwareAcceleration.Should().Be(HardwareAccelerationKind.Qsv); actual.HardwareAcceleration.Should().Be(HardwareAccelerationKind.Qsv);
} }

2
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs

@ -12,7 +12,7 @@ namespace ErsatzTV.Core.FFmpeg
public List<string> FormatFlags { get; set; } public List<string> FormatFlags { get; set; }
public HardwareAccelerationKind HardwareAcceleration { get; set; } public HardwareAccelerationKind HardwareAcceleration { get; set; }
public string VideoDecoder { get; set; } public string VideoDecoder { get; set; }
public bool RealtimeOutput => true; public bool RealtimeOutput { get; set; }
public Option<TimeSpan> StreamSeek { get; set; } public Option<TimeSpan> StreamSeek { get; set; }
public Option<IDisplaySize> ScaledSize { get; set; } public Option<IDisplaySize> ScaledSize { get; set; }
public bool PadToDesiredResolution { get; set; } public bool PadToDesiredResolution { get; set; }

14
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -51,14 +51,22 @@ namespace ErsatzTV.Core.FFmpeg
DateTimeOffset start, DateTimeOffset start,
DateTimeOffset now, DateTimeOffset now,
TimeSpan inPoint, TimeSpan inPoint,
TimeSpan outPoint) TimeSpan outPoint,
bool hlsRealtime)
{ {
var result = new FFmpegPlaybackSettings var result = new FFmpegPlaybackSettings
{ {
ThreadCount = ffmpegProfile.ThreadCount, FormatFlags = CommonFormatFlags,
FormatFlags = CommonFormatFlags RealtimeOutput = streamingMode switch
{
StreamingMode.HttpLiveStreamingSegmenter => hlsRealtime,
_ => true
}
}; };
// always use one thread with realtime output
result.ThreadCount = result.RealtimeOutput ? 1 : ffmpegProfile.ThreadCount;
if (now != start || inPoint != TimeSpan.Zero) if (now != start || inPoint != TimeSpan.Zero)
{ {
result.StreamSeek = now - start + inPoint; result.StreamSeek = now - start + inPoint;

9
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

@ -379,7 +379,14 @@ namespace ErsatzTV.Core.FFmpeg
_arguments.Add($"{format}"); _arguments.Add($"{format}");
return this; return this;
} }
public FFmpegProcessBuilder WithInitialDiscontinuity()
{
_arguments.Add("-mpegts_flags");
_arguments.Add("+initial_discontinuity");
return this;
}
public FFmpegProcessBuilder WithHls(string channelNumber, Option<MediaVersion> mediaVersion) public FFmpegProcessBuilder WithHls(string channelNumber, Option<MediaVersion> mediaVersion)
{ {
const int SEGMENT_SECONDS = 4; const int SEGMENT_SECONDS = 4;

11
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

@ -65,7 +65,8 @@ namespace ErsatzTV.Core.FFmpeg
start, start,
now, now,
inPoint, inPoint,
outPoint); outPoint,
hlsRealtime);
Option<WatermarkOptions> watermarkOptions = Option<WatermarkOptions> watermarkOptions =
await GetWatermarkOptions(channel, globalWatermark, videoVersion, None, None); await GetWatermarkOptions(channel, globalWatermark, videoVersion, None, None);
@ -158,10 +159,10 @@ namespace ErsatzTV.Core.FFmpeg
// HLS needs to segment and generate playlist // HLS needs to segment and generate playlist
case StreamingMode.HttpLiveStreamingSegmenter: case StreamingMode.HttpLiveStreamingSegmenter:
return builder.WithHls(channel.Number, videoVersion) return builder.WithHls(channel.Number, videoVersion)
.WithRealtimeOutput(hlsRealtime)
.Build(); .Build();
default: default:
return builder.WithFormat("mpegts") return builder.WithFormat("mpegts")
.WithInitialDiscontinuity()
.WithPipe() .WithPipe()
.Build(); .Build();
} }
@ -221,7 +222,6 @@ namespace ErsatzTV.Core.FFmpeg
// HLS needs to segment and generate playlist // HLS needs to segment and generate playlist
case StreamingMode.HttpLiveStreamingSegmenter: case StreamingMode.HttpLiveStreamingSegmenter:
return builder.WithHls(channel.Number, None) return builder.WithHls(channel.Number, None)
.WithRealtimeOutput(hlsRealtime)
.Build(); .Build();
default: default:
return builder.WithFormat("mpegts") return builder.WithFormat("mpegts")
@ -255,7 +255,7 @@ namespace ErsatzTV.Core.FFmpeg
.WithThreads(1) .WithThreads(1)
.WithQuiet() .WithQuiet()
.WithFormatFlags(playbackSettings.FormatFlags) .WithFormatFlags(playbackSettings.FormatFlags)
.WithRealtimeOutput(playbackSettings.RealtimeOutput) .WithRealtimeOutput(true)
.WithInput($"http://localhost:{Settings.ListenPort}/iptv/channel/{channel.Number}.m3u8?mode=segmenter") .WithInput($"http://localhost:{Settings.ListenPort}/iptv/channel/{channel.Number}.m3u8?mode=segmenter")
.WithMap("0") .WithMap("0")
.WithCopyCodec() .WithCopyCodec()
@ -335,7 +335,8 @@ namespace ErsatzTV.Core.FFmpeg
DateTimeOffset.UnixEpoch, DateTimeOffset.UnixEpoch,
DateTimeOffset.UnixEpoch, DateTimeOffset.UnixEpoch,
TimeSpan.Zero, TimeSpan.Zero,
TimeSpan.Zero); TimeSpan.Zero,
false);
FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, false, _logger) FFmpegProcessBuilder builder = new FFmpegProcessBuilder(ffmpegPath, false, _logger)
.WithThreads(1) .WithThreads(1)

Loading…
Cancel
Save