From f41f4b19d4564b13b659770307c828f538f778c8 Mon Sep 17 00:00:00 2001 From: Jason Dove <1695733+jasongdove@users.noreply.github.com> Date: Tue, 14 Oct 2025 11:45:11 -0500 Subject: [PATCH] wait for two initial segments in playback troubleshooting (#2532) --- CHANGELOG.md | 1 + ...layoutItemProcessByChannelNumberHandler.cs | 1 - .../PrepareTroubleshootingPlaybackHandler.cs | 1 - .../FFmpegPlaybackSettingsCalculatorTests.cs | 44 +------------------ .../FFmpeg/FFmpegLibraryProcessService.cs | 12 +++-- .../FFmpeg/FFmpegPlaybackSettings.cs | 2 +- .../FFmpegPlaybackSettingsCalculator.cs | 3 +- ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs | 1 - .../FFmpeg/IFFmpegProcessService.cs | 1 - .../PipelineBuilderBaseTests.cs | 6 +-- .../Pipeline/PipelineBuilderBase.cs | 2 +- ErsatzTV.FFmpeg/State/AudioState.cs | 4 +- .../Core/FFmpeg/TranscodingTests.cs | 2 - .../Controllers/Api/TroubleshootController.cs | 35 ++++++++++++--- 14 files changed, 45 insertions(+), 70 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index edc1b9759..f71953b06 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -33,6 +33,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Dramatically improve stream startup time - VAAPI: fix scaling image-based subtitles (e.g. dvdsub) - Fix HLS Segmenter (fmp4) on Windows +- Playback troubleshooting: wait for at least 2 initial segments (up to configured initial segment count) to reduce stalls ### Changed - Do not use graphics engine for single, permanent watermark diff --git a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs index 4d89d58b7..5f0701562 100644 --- a/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs +++ b/ErsatzTV.Application/Streaming/Queries/GetPlayoutItemProcessByChannelNumberHandler.cs @@ -430,7 +430,6 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler< : StreamInputKind.Vod, playoutItemWithPath.PlayoutItem.FillerKind, inPoint, - outPoint, request.ChannelStartTime, request.PtsOffset, request.TargetFramerate, diff --git a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs index 57d6bc0ad..dfd903d44 100644 --- a/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs +++ b/ErsatzTV.Application/Troubleshooting/Commands/PrepareTroubleshootingPlaybackHandler.cs @@ -246,7 +246,6 @@ public class PrepareTroubleshootingPlaybackHandler( mediaItem is RemoteStream { IsLive: true } ? StreamInputKind.Live : StreamInputKind.Vod, FillerKind.None, inPoint, - outPoint, channelStartTime: DateTimeOffset.Now, 0, None, diff --git a/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs b/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs index 36ee3f5b8..2fbd4e2ae 100644 --- a/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs +++ b/ErsatzTV.Core.Tests/FFmpeg/FFmpegPlaybackSettingsCalculatorTests.cs @@ -29,7 +29,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -50,7 +49,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -75,7 +73,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -96,7 +93,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -117,7 +113,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -138,7 +133,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -161,7 +155,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -184,7 +177,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -205,7 +197,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -228,7 +219,6 @@ public class FFmpegPlaybackSettingsCalculatorTests now, now.AddMinutes(5), TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -252,7 +242,6 @@ public class FFmpegPlaybackSettingsCalculatorTests now, now.AddMinutes(5), TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -280,7 +269,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -307,7 +295,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -334,7 +321,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -362,7 +348,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -389,7 +374,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -419,7 +403,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -450,7 +433,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -480,7 +462,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -510,7 +491,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -540,7 +520,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -572,7 +551,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -603,7 +581,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -634,7 +611,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -664,7 +640,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -695,7 +670,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -725,7 +699,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -756,7 +729,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -782,7 +754,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -806,7 +777,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -830,7 +800,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -855,7 +824,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -880,7 +848,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -905,7 +872,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -930,7 +896,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -954,7 +919,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -978,7 +942,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -987,7 +950,7 @@ public class FFmpegPlaybackSettingsCalculatorTests } [Test] - public void Should_SetAudioDuration_With_CorrectFormat_ForTransportStream() + public void Should_SetPadAudio_ForTransportStream() { FFmpegProfile ffmpegProfile = TestProfile() with { @@ -1008,12 +971,11 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.FromMinutes(2), false, StreamInputKind.Vod, None); - actual.AudioDuration.IfNone(TimeSpan.MinValue).ShouldBe(TimeSpan.FromMinutes(2)); + actual.PadAudio.ShouldBe(true); } [Test] @@ -1032,7 +994,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); @@ -1058,7 +1019,6 @@ public class FFmpegPlaybackSettingsCalculatorTests DateTimeOffset.Now, DateTimeOffset.Now, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, None); diff --git a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs index 24a4eaf41..b390c2bce 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegLibraryProcessService.cs @@ -81,7 +81,6 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService StreamInputKind streamInputKind, FillerKind fillerKind, TimeSpan inPoint, - TimeSpan outPoint, DateTimeOffset channelStartTime, long ptsOffset, Option targetFramerate, @@ -102,7 +101,6 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService start, now, inPoint, - outPoint, hlsRealtime, streamInputKind, targetFramerate); @@ -184,7 +182,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playbackSettings.AudioBitrate, playbackSettings.AudioBufferSize, playbackSettings.AudioSampleRate, - videoPath == audioPath ? playbackSettings.AudioDuration : Option.None, + videoPath == audioPath, playbackSettings.NormalizeLoudnessMode switch { NormalizeLoudnessMode.LoudNorm => AudioFilter.LoudNorm, @@ -239,7 +237,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService // when no audio streams are available, use null audio source if (!audioVersion.MediaVersion.Streams.Any(s => s.MediaStreamKind is MediaStreamKind.Audio)) { - audioInputFile = new NullAudioInputFile(audioState with { AudioDuration = playbackSettings.AudioDuration }); + audioInputFile = new NullAudioInputFile(audioState with { PadAudio = playbackSettings.PadAudio }); } OutputFormatKind outputFormat = OutputFormatKind.MpegTs; @@ -599,7 +597,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playbackSettings.AudioBitrate, playbackSettings.AudioBufferSize, playbackSettings.AudioSampleRate, - Option.None, + false, AudioFilter.None); string videoFormat = GetVideoFormat(playbackSettings); @@ -789,7 +787,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService channel.FFmpegProfile, Option.None); - playbackSettings.AudioDuration = Option.None; + playbackSettings.PadAudio = false; string audioFormat = playbackSettings.AudioFormat switch { @@ -805,7 +803,7 @@ public class FFmpegLibraryProcessService : IFFmpegProcessService playbackSettings.AudioBitrate, playbackSettings.AudioBufferSize, playbackSettings.AudioSampleRate, - Option.None, + false, playbackSettings.NormalizeLoudnessMode switch { // TODO: NormalizeLoudnessMode.LoudNorm => AudioFilter.LoudNorm, diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs index 0a7212517..0c44926f7 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettings.cs @@ -23,7 +23,7 @@ public class FFmpegPlaybackSettings public Option AudioBufferSize { get; set; } public Option AudioChannels { get; set; } public Option AudioSampleRate { get; set; } - public Option AudioDuration { get; set; } + public bool PadAudio { get; set; } public FFmpegProfileAudioFormat AudioFormat { get; set; } public bool Deinterlace { get; set; } public Option VideoTrackTimeScale { get; set; } diff --git a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs index 25470eed4..556e94b76 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegPlaybackSettingsCalculator.cs @@ -50,7 +50,6 @@ public static class FFmpegPlaybackSettingsCalculator DateTimeOffset start, DateTimeOffset now, TimeSpan inPoint, - TimeSpan outPoint, bool hlsRealtime, StreamInputKind streamInputKind, Option targetFramerate) @@ -168,7 +167,7 @@ public static class FFmpegPlaybackSettingsCalculator result.AudioBufferSize = ffmpegProfile.AudioBufferSize; result.AudioChannels = ffmpegProfile.AudioChannels; result.AudioSampleRate = ffmpegProfile.AudioSampleRate; - result.AudioDuration = outPoint - inPoint; + result.PadAudio = true; result.NormalizeLoudnessMode = ffmpegProfile.NormalizeLoudnessMode; result.Deinterlace = ffmpegProfile.DeinterlaceVideo == true && diff --git a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs index a85cd3d41..02d36f4fa 100644 --- a/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs +++ b/ErsatzTV.Core/FFmpeg/FFmpegProcessService.cs @@ -84,7 +84,6 @@ public class FFmpegProcessService DateTimeOffset.UnixEpoch, DateTimeOffset.UnixEpoch, TimeSpan.Zero, - TimeSpan.Zero, false, StreamInputKind.Vod, Option.None); diff --git a/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs b/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs index 295aa1797..c7daf397b 100644 --- a/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs +++ b/ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegProcessService.cs @@ -36,7 +36,6 @@ public interface IFFmpegProcessService StreamInputKind streamInputKind, FillerKind fillerKind, TimeSpan inPoint, - TimeSpan outPoint, DateTimeOffset channelStartTime, long ptsOffset, Option targetFramerate, diff --git a/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs b/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs index 2e6e713e9..85db1e7e7 100644 --- a/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs +++ b/ErsatzTV.FFmpeg.Tests/PipelineBuilderBaseTests.cs @@ -52,7 +52,7 @@ public class PipelineBuilderBaseTests 320, 640, 48, - Option.None, + false, AudioFilter.None)); var desiredState = new FrameState( @@ -151,7 +151,7 @@ public class PipelineBuilderBaseTests 320, 640, 48, - Option.None, + false, AudioFilter.None)); var desiredState = new FrameState( @@ -308,7 +308,7 @@ public class PipelineBuilderBaseTests None, None, None, - None, + false, AudioFilter.None)); var desiredState = new FrameState( diff --git a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs index 7da83184b..2fa76df58 100644 --- a/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs +++ b/ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs @@ -467,7 +467,7 @@ public abstract class PipelineBuilderBase : IPipelineBuilder audioInputFile.FilterSteps.Add(new AudioResampleFilter()); } - foreach (TimeSpan _ in audioInputFile.DesiredState.AudioDuration) + if (audioInputFile.DesiredState.PadAudio) { audioInputFile.FilterSteps.Add(new AudioPadFilter()); } diff --git a/ErsatzTV.FFmpeg/State/AudioState.cs b/ErsatzTV.FFmpeg/State/AudioState.cs index 9ca98952c..388103513 100644 --- a/ErsatzTV.FFmpeg/State/AudioState.cs +++ b/ErsatzTV.FFmpeg/State/AudioState.cs @@ -6,7 +6,7 @@ public record AudioState( Option AudioBitrate, Option AudioBufferSize, Option AudioSampleRate, - Option AudioDuration, + bool PadAudio, AudioFilter NormalizeLoudnessFilter) { public static readonly AudioState Copy = new( @@ -15,7 +15,7 @@ public record AudioState( Option.None, Option.None, Option.None, - Option.None, + false, AudioFilter.None ); } diff --git a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs index 320908c5d..a33fb85b0 100644 --- a/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs +++ b/ErsatzTV.Scanner.Tests/Core/FFmpeg/TranscodingTests.cs @@ -395,7 +395,6 @@ public class TranscodingTests StreamInputKind.Vod, FillerKind.None, TimeSpan.Zero, - TimeSpan.FromSeconds(3), DateTimeOffset.Now, 0, None, @@ -707,7 +706,6 @@ public class TranscodingTests StreamInputKind.Vod, FillerKind.None, TimeSpan.Zero, - TimeSpan.FromSeconds(3), DateTimeOffset.Now, 0, None, diff --git a/ErsatzTV/Controllers/Api/TroubleshootController.cs b/ErsatzTV/Controllers/Api/TroubleshootController.cs index 390aea054..8edb91a3d 100644 --- a/ErsatzTV/Controllers/Api/TroubleshootController.cs +++ b/ErsatzTV/Controllers/Api/TroubleshootController.cs @@ -7,6 +7,7 @@ using ErsatzTV.Core; using ErsatzTV.Core.Domain; using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.Metadata; +using ErsatzTV.Core.Interfaces.Repositories; using ErsatzTV.Core.Interfaces.Troubleshooting; using MediatR; using Microsoft.AspNetCore.Mvc; @@ -18,6 +19,7 @@ namespace ErsatzTV.Controllers.Api; public class TroubleshootController( ChannelWriter channelWriter, ILocalFileSystem localFileSystem, + IConfigElementRepository configElementRepository, ITroubleshootingNotifier notifier, IMediator mediator) : ControllerBase { @@ -94,20 +96,41 @@ public class TroubleshootController( troubleshootingInfo), cancellationToken); - string playlistFile = Path.Combine( - FileSystemLayout.TranscodeFolder, - ".troubleshooting", - "live.m3u8"); - + string playlistFile = Path.Combine(FileSystemLayout.TranscodeTroubleshootingFolder, "live.m3u8"); while (!localFileSystem.FileExists(playlistFile)) { - await Task.Delay(TimeSpan.FromMilliseconds(250), cancellationToken); + await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken); if (cancellationToken.IsCancellationRequested || notifier.IsFailed(sessionId)) { break; } } + int initialSegmentCount = await configElementRepository + .GetValue(ConfigElementKey.FFmpegInitialSegmentCount, cancellationToken) + .Map(maybeCount => maybeCount.Match(c => c, () => 1)); + + initialSegmentCount = Math.Max(initialSegmentCount, 2); + + bool hasSegments = false; + while (!hasSegments) + { + await Task.Delay(TimeSpan.FromMilliseconds(100), cancellationToken); + + string[] segmentFiles = streamingMode switch + { + StreamingMode.HttpLiveStreamingSegmenterFmp4 => Directory.GetFiles( + FileSystemLayout.TranscodeTroubleshootingFolder, + "*.m4s"), + _ => Directory.GetFiles(FileSystemLayout.TranscodeTroubleshootingFolder, "*.ts") + }; + + if (segmentFiles.Length >= initialSegmentCount) + { + hasSegments = true; + } + } + if (!notifier.IsFailed(sessionId)) { return Redirect("~/iptv/session/.troubleshooting/live.m3u8");