From 53cad42e766a8b66dc98053cb4b98657e0f5d00c Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Sun, 15 Feb 2026 09:09:02 -0600 Subject: [PATCH] refactor --- .../FFmpeg/FFmpegLibraryProcessService.cs | 209 +++++++----------- .../FFmpegPlaybackSettingsCalculator.cs | 2 +- ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs | 2 +- 3 files changed, 86 insertions(+), 127 deletions(-) diff --git a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs index efd9f7c5f..22584711f 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs @@ -665,7 +665,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService string vaapiDevice, Option qsvExtraHardwareFrames) { - FFmpegPlaybackSettings playbackSettings = FFmpegPlaybackSettingsCalculator.CalculateErrorSettings( + FFmpegPlaybackSettings playbackSettings = FFmpegPlaybackSettingsCalculator.CalculateGeneratedImageSettings( channel.StreamingMode, channel.FFmpegProfile, hlsRealtime); @@ -726,57 +726,6 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playbackSettings.NormalizeColors, playbackSettings.Deinterlace); - OutputFormatKind outputFormat = OutputFormatKind.MpegTs; - switch (channel.StreamingMode) - { - case StreamingMode.HttpLiveStreamingSegmenter: - outputFormat = OutputFormatKind.Hls; - break; - } - - Option hlsPlaylistPath = outputFormat is OutputFormatKind.Hls or OutputFormatKind.HlsMp4 - ? Path.Combine(FileSystemLayout.TranscodeFolder, channel.Number, "live.m3u8") - : Option.None; - - long nowSeconds = now.ToUnixTimeSeconds(); - - Option hlsSegmentTemplate = outputFormat switch - { - OutputFormatKind.Hls => Path.Combine(FileSystemLayout.TranscodeFolder, channel.Number, "live%06d.ts"), - OutputFormatKind.HlsMp4 => Path.Combine( - FileSystemLayout.TranscodeFolder, - channel.Number, - $"live_{nowSeconds}_%06d.m4s"), - _ => Option.None - }; - - Option hlsInitTemplate = outputFormat switch - { - OutputFormatKind.HlsMp4 => $"{nowSeconds}_init.mp4", - _ => Option.None - }; - - Option hlsSegmentOptions = Option.None; - if (outputFormat is OutputFormatKind.Hls) - { - string options = string.Empty; - - if (ptsOffset == TimeSpan.Zero) - { - options += "+initial_discontinuity"; - } - - if (audioFormat == AudioFormat.AacLatm) - { - options += "+latm"; - } - - if (!string.IsNullOrWhiteSpace(options)) - { - hlsSegmentOptions = $"mpegts_flags={options}"; - } - } - string videoPath = _localFileSystem.GetCustomOrDefaultFile( FileSystemLayout.ResourcesCacheFolder, "background.png"); @@ -802,6 +751,8 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService HardwareAccelerationMode hwAccel = GetHardwareAccelerationMode(playbackSettings); _logger.LogDebug("HW accel mode: {HwAccel}", hwAccel); + var hlsOptions = GetHlsOptions(channel, now, ptsOffset, audioFormat); + var ffmpegState = new FFmpegState( channel.Number == FileSystemLayout.TranscodeTroubleshootingChannel, HardwareAccelerationMode.None, // no hw accel decode since errors loop @@ -816,11 +767,11 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService None, None, None, - outputFormat, - hlsPlaylistPath, - hlsSegmentTemplate, - hlsInitTemplate, - hlsSegmentOptions, + hlsOptions.OutputFormat, + hlsOptions.HlsPlaylistPath, + hlsOptions.HlsSegmentTemplate, + hlsOptions.HlsInitTemplate, + hlsOptions.HlsSegmentOptions, ptsOffset, Option.None, qsvExtraHardwareFrames, @@ -870,7 +821,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService bool hlsRealtime, TimeSpan ptsOffset) { - FFmpegPlaybackSettings playbackSettings = FFmpegPlaybackSettingsCalculator.CalculateErrorSettings( + FFmpegPlaybackSettings playbackSettings = FFmpegPlaybackSettingsCalculator.CalculateGeneratedImageSettings( channel.StreamingMode, channel.FFmpegProfile, hlsRealtime); @@ -911,70 +862,13 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService ? FFmpegFilterMode.HardwareIfPossible : FFmpegFilterMode.Software, IsAnamorphic: false, - playbackSettings.FrameRate, + Option.None, playbackSettings.VideoBitrate, playbackSettings.VideoBufferSize, playbackSettings.VideoTrackTimeScale, playbackSettings.NormalizeColors, playbackSettings.Deinterlace); - OutputFormatKind outputFormat = OutputFormatKind.MpegTs; - switch (channel.StreamingMode) - { - case StreamingMode.HttpLiveStreamingSegmenter: - outputFormat = OutputFormatKind.Hls; - break; - } - - Option hlsPlaylistPath = outputFormat is OutputFormatKind.Hls or OutputFormatKind.HlsMp4 - ? Path.Combine(FileSystemLayout.TranscodeFolder, channel.Number, "live.m3u8") - : Option.None; - - long nowSeconds = now.ToUnixTimeSeconds(); - - Option hlsSegmentTemplate = outputFormat switch - { - OutputFormatKind.Hls => Path.Combine(FileSystemLayout.TranscodeFolder, channel.Number, "live%06d.ts"), - OutputFormatKind.HlsMp4 => Path.Combine( - FileSystemLayout.TranscodeFolder, - channel.Number, - $"live_{nowSeconds}_%06d.m4s"), - _ => Option.None - }; - - Option hlsInitTemplate = outputFormat switch - { - OutputFormatKind.HlsMp4 => $"{nowSeconds}_init.mp4", - _ => Option.None - }; - - Option hlsSegmentOptions = Option.None; - if (outputFormat is OutputFormatKind.Hls) - { - string options = string.Empty; - - if (ptsOffset == TimeSpan.Zero) - { - options += "+initial_discontinuity"; - } - - if (audioFormat == AudioFormat.AacLatm) - { - options += "+latm"; - } - - if (!string.IsNullOrWhiteSpace(options)) - { - hlsSegmentOptions = $"mpegts_flags={options}"; - } - } - - string videoPath = _localFileSystem.GetCustomOrDefaultFile( - FileSystemLayout.ResourcesCacheFolder, - "background.png"); - - var videoVersion = BackgroundImageMediaVersion.ForPath(videoPath, desiredResolution); - var frameRate = playbackSettings.FrameRate.IfNone(new FrameRate("24")); var ffmpegVideoStream = new VideoStream( @@ -983,19 +877,21 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService string.Empty, new PixelFormatUnknown(), // leave this unknown so we convert to desired yuv420p ColorParams.Default, - new FrameSize(videoVersion.Width, videoVersion.Height), - videoVersion.SampleAspectRatio, - videoVersion.DisplayAspectRatio, + desiredState.PaddedSize, + MaybeSampleAspectRatio: "1:1", + DisplayAspectRatio: string.Empty, frameRate, true, ScanKind.Progressive); var videoInputFile = new LavfiInputFile( - $"color=c=black:s={videoVersion.Width}x{videoVersion.Height}:r={frameRate.FrameRateString}:d={duration.TotalSeconds}", + $"color=c=black:s={desiredState.PaddedSize.Width}x{desiredState.PaddedSize.Height}:r={frameRate.FrameRateString}:d={duration.TotalSeconds}", ffmpegVideoStream); HardwareAccelerationMode hwAccel = GetHardwareAccelerationMode(playbackSettings); + var hlsOptions = GetHlsOptions(channel, now, ptsOffset, audioFormat); + var ffmpegState = new FFmpegState( channel.Number == FileSystemLayout.TranscodeTroubleshootingChannel, HardwareAccelerationMode.None, // no hw accel decode since errors loop @@ -1010,11 +906,11 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService None, None, None, - outputFormat, - hlsPlaylistPath, - hlsSegmentTemplate, - hlsInitTemplate, - hlsSegmentOptions, + hlsOptions.OutputFormat, + hlsOptions.HlsPlaylistPath, + hlsOptions.HlsSegmentTemplate, + hlsOptions.HlsInitTemplate, + hlsOptions.HlsSegmentOptions, ptsOffset, Option.None, Optional(channel.FFmpegProfile.QsvExtraHardwareFrames), @@ -1445,4 +1341,67 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService return false; } + + private static HlsOptions GetHlsOptions(Channel channel, DateTimeOffset now, TimeSpan ptsOffset, string audioFormat) + { + OutputFormatKind outputFormat = OutputFormatKind.MpegTs; + switch (channel.StreamingMode) + { + case StreamingMode.HttpLiveStreamingSegmenter: + outputFormat = OutputFormatKind.Hls; + break; + } + + Option hlsPlaylistPath = outputFormat is OutputFormatKind.Hls or OutputFormatKind.HlsMp4 + ? Path.Combine(FileSystemLayout.TranscodeFolder, channel.Number, "live.m3u8") + : Option.None; + + long nowSeconds = now.ToUnixTimeSeconds(); + + Option hlsSegmentTemplate = outputFormat switch + { + OutputFormatKind.Hls => Path.Combine(FileSystemLayout.TranscodeFolder, channel.Number, "live%06d.ts"), + OutputFormatKind.HlsMp4 => Path.Combine( + FileSystemLayout.TranscodeFolder, + channel.Number, + $"live_{nowSeconds}_%06d.m4s"), + _ => Option.None + }; + + Option hlsInitTemplate = outputFormat switch + { + OutputFormatKind.HlsMp4 => $"{nowSeconds}_init.mp4", + _ => Option.None + }; + + Option hlsSegmentOptions = Option.None; + if (outputFormat is OutputFormatKind.Hls) + { + string options = string.Empty; + + if (ptsOffset == TimeSpan.Zero) + { + options += "+initial_discontinuity"; + } + + if (audioFormat == AudioFormat.AacLatm) + { + options += "+latm"; + } + + if (!string.IsNullOrWhiteSpace(options)) + { + hlsSegmentOptions = $"mpegts_flags={options}"; + } + } + + return new HlsOptions(outputFormat, hlsPlaylistPath, hlsSegmentTemplate, hlsInitTemplate, hlsSegmentOptions); + } + + private sealed record HlsOptions( + OutputFormatKind OutputFormat, + Option HlsPlaylistPath, + Option HlsSegmentTemplate, + Option HlsInitTemplate, + Option HlsSegmentOptions); } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs index 812da70de..c200ecada 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs @@ -181,7 +181,7 @@ public static class FFmpegPlaybackSettingsCalculator return result; } - public static FFmpegPlaybackSettings CalculateErrorSettings( + public static FFmpegPlaybackSettings CalculateGeneratedImageSettings( StreamingMode streamingMode, FFmpegProfile ffmpegProfile, bool hlsRealtime) => diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs index 42ced7862..f4ea620b5 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs @@ -71,7 +71,7 @@ public class FFmpegProcessService } FFmpegPlaybackSettings playbackSettings = - FFmpegPlaybackSettingsCalculator.CalculateErrorSettings( + FFmpegPlaybackSettingsCalculator.CalculateGeneratedImageSettings( StreamingMode.TransportStream, channel.FFmpegProfile, false);