Browse Source

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

pull/581/head
Jason Dove 4 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. @@ -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/).
## [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
### Fixed

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

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

2
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs

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

14
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -51,14 +51,22 @@ namespace ErsatzTV.Core.FFmpeg @@ -51,14 +51,22 @@ namespace ErsatzTV.Core.FFmpeg
DateTimeOffset start,
DateTimeOffset now,
TimeSpan inPoint,
TimeSpan outPoint)
TimeSpan outPoint,
bool hlsRealtime)
{
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)
{
result.StreamSeek = now - start + inPoint;

9
ErsatzTV.Core/FFmpeg/FFmpegProcessBuilder.cs

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

11
ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs

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

Loading…
Cancel
Save