Browse Source

unlock ffmpeg thread count (#812)

* revert logging changes

* unlock ffmpeg thread count
pull/813/head
Jason Dove 4 years ago committed by GitHub
parent
commit
fd36ea51a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      CHANGELOG.md
  2. 18
      ErsatzTV.Application/Streaming/HlsSessionWorker.cs
  3. 8
      ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs
  4. 4
      ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs
  5. 17
      ErsatzTV.Core/FFmpeg/HlsPlaylistFilter.cs
  6. 15
      ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs
  7. 6
      ErsatzTV.FFmpeg/FFmpegState.cs
  8. 22
      ErsatzTV.FFmpeg/PipelineBuilder.cs

4
CHANGELOG.md

@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). @@ -10,6 +10,10 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
### Added
- Clean transcode cache folder on startup and after `HLS Segmenter` session terminates for any reason
### Changed
- Remove thread limitation for scenarios where it is not required
- This should give a performance boost to installations that don't use hardware acceleration
## [0.5.7-beta] - 2022-05-14
### Fixed
- Reduce memory use due to library scan operations

18
ErsatzTV.Application/Streaming/HlsSessionWorker.cs

@ -28,10 +28,10 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -28,10 +28,10 @@ public class HlsSessionWorker : IHlsSessionWorker
private string _channelNumber;
private bool _firstProcess;
private DateTimeOffset _lastAccess;
private DateTimeOffset _playlistStart;
private Option<int> _targetFramerate;
private Timer _timer;
private DateTimeOffset _transcodedUntil;
private DateTimeOffset _playlistStart;
public HlsSessionWorker(
IHlsPlaylistFilter hlsPlaylistFilter,
@ -357,14 +357,14 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -357,14 +357,14 @@ public class HlsSessionWorker : IHlsSessionWorker
.ToList();
var toDelete = allSegments.Filter(s => s.SequenceNumber < trimResult.Sequence).ToList();
if (toDelete.Count > 0)
{
_logger.LogDebug(
"Deleting HLS segments {Min} to {Max} (less than {StartSequence})",
toDelete.Map(s => s.SequenceNumber).Min(),
toDelete.Map(s => s.SequenceNumber).Max(),
trimResult.Sequence);
}
// if (toDelete.Count > 0)
// {
// _logger.LogDebug(
// "Deleting HLS segments {Min} to {Max} (less than {StartSequence})",
// toDelete.Map(s => s.SequenceNumber).Min(),
// toDelete.Map(s => s.SequenceNumber).Max(),
// trimResult.Sequence);
// }
foreach (Segment segment in toDelete)
{

8
ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs

@ -234,7 +234,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -234,7 +234,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
outputFormat,
hlsPlaylistPath,
hlsSegmentTemplate,
ptsOffset);
ptsOffset,
playbackSettings.ThreadCount);
_logger.LogDebug("FFmpeg desired state {FrameState}", desiredState);
@ -327,7 +328,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -327,7 +328,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
var ffmpegVideoStream = new VideoStream(
0,
VideoFormat.GeneratedImage,
new PixelFormatYuv420P(),
new PixelFormatUnknown(), // leave this unknown so we convert to desired yuv420p
new FrameSize(videoVersion.Width, videoVersion.Height),
None,
true);
@ -348,7 +349,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService @@ -348,7 +349,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService
outputFormat,
hlsPlaylistPath,
hlsSegmentTemplate,
ptsOffset);
ptsOffset,
Option<int>.None);
var ffmpegSubtitleStream = new ErsatzTV.FFmpeg.MediaStream(0, "ass", StreamKind.Video);

4
ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs

@ -177,7 +177,6 @@ public class FFmpegPlaybackSettingsCalculator @@ -177,7 +177,6 @@ public class FFmpegPlaybackSettingsCalculator
new()
{
HardwareAcceleration = HardwareAccelerationKind.None,
ThreadCount = 1,
FormatFlags = CommonFormatFlags,
VideoFormat = ffmpegProfile.VideoFormat,
VideoBitrate = ffmpegProfile.VideoBitrate,
@ -192,7 +191,8 @@ public class FFmpegPlaybackSettingsCalculator @@ -192,7 +191,8 @@ public class FFmpegPlaybackSettingsCalculator
StreamingMode.HttpLiveStreamingSegmenter => hlsRealtime,
_ => true
},
VideoTrackTimeScale = 90000
VideoTrackTimeScale = 90000,
FrameRate = 24
};
private static bool NeedToScale(FFmpegProfile ffmpegProfile, MediaVersion version) =>

17
ErsatzTV.Core/FFmpeg/HlsPlaylistFilter.cs

@ -25,13 +25,13 @@ public class HlsPlaylistFilter : IHlsPlaylistFilter @@ -25,13 +25,13 @@ public class HlsPlaylistFilter : IHlsPlaylistFilter
{
try
{
_logger.LogDebug(
"TrimPlaylist - Start {PlaylistStart}, FilterBefore {FilterBefore}, MaxSegments {MaxSegments}, EndWithDiscontinuity {EndWithDiscontinuity}",
playlistStart,
filterBefore,
maxSegments,
endWithDiscontinuity);
// _logger.LogDebug(
// "TrimPlaylist - Start {PlaylistStart}, FilterBefore {FilterBefore}, MaxSegments {MaxSegments}, EndWithDiscontinuity {EndWithDiscontinuity}",
// playlistStart,
// filterBefore,
// maxSegments,
// endWithDiscontinuity);
DateTimeOffset currentTime = playlistStart;
DateTimeOffset nextPlaylistStart = DateTimeOffset.MaxValue;
@ -120,8 +120,7 @@ public class HlsPlaylistFilter : IHlsPlaylistFilter @@ -120,8 +120,7 @@ public class HlsPlaylistFilter : IHlsPlaylistFilter
playlist += "#EXT-X-DISCONTINUITY" + Environment.NewLine;
}
// if (playlist.Trim().Split(Environment.NewLine).All(l => string.IsNullOrWhiteSpace(l) || l.StartsWith('#')))
if (playlist.Trim().Split(Environment.NewLine).All(l => l.StartsWith('#')))
if (playlist.Trim().Split(Environment.NewLine).All(l => string.IsNullOrWhiteSpace(l) || l.StartsWith('#')))
{
throw new Exception("Trimming playlist to nothing");
}

15
ErsatzTV.FFmpeg.Tests/PipelineBuilderTests.cs

@ -56,7 +56,7 @@ public class PipelineGeneratorTests @@ -56,7 +56,7 @@ public class PipelineGeneratorTests
HardwareAccelerationMode.None,
Option<string>.None,
Option<string>.None,
Option<TimeSpan>.None,
TimeSpan.FromSeconds(1),
Option<TimeSpan>.None,
false,
Option<string>.None,
@ -65,7 +65,8 @@ public class PipelineGeneratorTests @@ -65,7 +65,8 @@ public class PipelineGeneratorTests
OutputFormatKind.MpegTs,
Option<string>.None,
Option<string>.None,
0);
0,
Option<int>.None);
var builder = new PipelineBuilder(videoInputFile, audioInputFile, None, None, "", "", _logger);
FFmpegPipeline result = builder.Build(ffmpegState, desiredState);
@ -73,7 +74,8 @@ public class PipelineGeneratorTests @@ -73,7 +74,8 @@ public class PipelineGeneratorTests
result.PipelineSteps.Should().HaveCountGreaterThan(0);
result.PipelineSteps.Should().Contain(ps => ps is EncoderLibx265);
PrintCommand(videoInputFile, audioInputFile, None, None, result);
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:a aac -ac 2 -b:a 320k -maxrate:a 320k -bufsize:a 640k -ar 48k -c:v libx265 -tag:v hvc1 -x265-params log-level=error -f mpegts -mpegts_flags +initial_discontinuity pipe:1");
}
[Test]
@ -90,7 +92,7 @@ public class PipelineGeneratorTests @@ -90,7 +92,7 @@ public class PipelineGeneratorTests
string command = PrintCommand(None, None, None, concatInputFile, result);
command.Should().Be(
"-threads 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 -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 -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");
}
[Test]
@ -140,7 +142,8 @@ public class PipelineGeneratorTests @@ -140,7 +142,8 @@ public class PipelineGeneratorTests
OutputFormatKind.MpegTs,
Option<string>.None,
Option<string>.None,
0);
0,
Option<int>.None);
var builder = new PipelineBuilder(videoInputFile, audioInputFile, None, None, "", "", _logger);
FFmpegPipeline result = builder.Build(ffmpegState, desiredState);
@ -152,7 +155,7 @@ public class PipelineGeneratorTests @@ -152,7 +155,7 @@ public class PipelineGeneratorTests
string command = PrintCommand(videoInputFile, audioInputFile, None, None, result);
command.Should().Be(
"-threads 1 -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 -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]

6
ErsatzTV.FFmpeg/FFmpegState.cs

@ -16,7 +16,8 @@ public record FFmpegState( @@ -16,7 +16,8 @@ public record FFmpegState(
OutputFormatKind OutputFormat,
Option<string> HlsPlaylistPath,
Option<string> HlsSegmentTemplate,
long PtsOffset)
long PtsOffset,
Option<int> ThreadCount)
{
public static FFmpegState Concat(bool saveReport, string channelName) =>
new(
@ -33,5 +34,6 @@ public record FFmpegState( @@ -33,5 +34,6 @@ public record FFmpegState(
OutputFormatKind.MpegTs,
Option<string>.None,
Option<string>.None,
0);
0,
Option<int>.None);
}

22
ErsatzTV.FFmpeg/PipelineBuilder.cs

@ -36,7 +36,6 @@ public class PipelineBuilder @@ -36,7 +36,6 @@ public class PipelineBuilder
{
_pipelineSteps = new List<IPipelineStep>
{
new ThreadCountOption(1), // try everything single-threaded
new NoStandardInputOption(),
new HideBannerOption(),
new NoStatsOption(),
@ -80,6 +79,11 @@ public class PipelineBuilder @@ -80,6 +79,11 @@ public class PipelineBuilder
concatInputFile.AddOption(new RealtimeInputOption());
concatInputFile.AddOption(new InfiniteLoopInputOption(HardwareAccelerationMode.None));
foreach (int threadCount in ffmpegState.ThreadCount)
{
_pipelineSteps.Insert(0, new ThreadCountOption(threadCount));
}
_pipelineSteps.Add(new NoSceneDetectOutputOption(0));
_pipelineSteps.Add(new EncoderCopyAll());
@ -111,6 +115,22 @@ public class PipelineBuilder @@ -111,6 +115,22 @@ public class PipelineBuilder
public FFmpegPipeline Build(FFmpegState ffmpegState, FrameState desiredState)
{
if (ffmpegState.Start.Exists(s => s > TimeSpan.Zero) && desiredState.Realtime)
{
_logger.LogInformation(
"Forcing {Threads} ffmpeg thread due to buggy combination of stream seek and realtime output",
1);
_pipelineSteps.Insert(0, new ThreadCountOption(1));
}
else
{
foreach (int threadCount in ffmpegState.ThreadCount)
{
_pipelineSteps.Insert(0, new ThreadCountOption(threadCount));
}
}
var allVideoStreams = _videoInputFile.SelectMany(f => f.VideoStreams).ToList();
// -sc_threshold 0 is unsupported with mpeg2video

Loading…
Cancel
Save