Browse Source

fix mpegts wrapper with fmp4 segmenter source (#2545)

pull/2546/head
Jason Dove 3 months ago committed by GitHub
parent
commit
35e7922836
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      ErsatzTV.Application/Streaming/Commands/StartFFmpegSessionHandler.cs
  2. 39
      ErsatzTV.Application/Streaming/HlsSessionWorker.cs
  3. 4
      ErsatzTV.Core/FFmpeg/FFmpegSegmenterService.cs
  4. 2
      ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegSegmenterService.cs
  5. 2
      ErsatzTV.Core/Interfaces/FFmpeg/IHlsSessionWorker.cs
  6. 5
      ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs
  7. 2
      ErsatzTV/Services/FFmpegWorkerService.cs

2
ErsatzTV.Application/Streaming/Commands/StartFFmpegSessionHandler.cs

@ -145,7 +145,7 @@ public class StartFFmpegSessionHandler : IRequestHandler<StartFFmpegSession, Eit @@ -145,7 +145,7 @@ public class StartFFmpegSessionHandler : IRequestHandler<StartFFmpegSession, Eit
request.ChannelNumber,
out IHlsSessionWorker worker))
{
worker?.Touch();
worker?.Touch(Option<string>.None);
}
return result.AsTask();

39
ErsatzTV.Application/Streaming/HlsSessionWorker.cs

@ -1,4 +1,5 @@ @@ -1,4 +1,5 @@
using System.Diagnostics;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO.Pipelines;
@ -48,6 +49,7 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -48,6 +49,7 @@ public class HlsSessionWorker : IHlsSessionWorker
private Timer _timer;
private DateTimeOffset _transcodedUntil;
private string _workingDirectory;
private ConcurrentDictionary<string, DateTimeOffset> _initTouches = new();
public HlsSessionWorker(
IServiceScopeFactory serviceScopeFactory,
@ -89,7 +91,7 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -89,7 +91,7 @@ public class HlsSessionWorker : IHlsSessionWorker
}
}
public void Touch()
public void Touch(Option<string> fileName)
{
lock (_sync)
{
@ -97,6 +99,12 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -97,6 +99,12 @@ public class HlsSessionWorker : IHlsSessionWorker
_lastAccess = DateTimeOffset.Now;
foreach (string init in fileName.Where(f => f.Contains("init.mp4")))
{
//_logger.LogDebug("Keep alive init {Init} for channel {ChannelNumber}", init, _channelNumber);
_initTouches.AddOrUpdate(init, DateTimeOffset.Now, (_, _) => DateTimeOffset.Now);
}
_timer?.Stop();
_timer?.Start();
}
@ -195,7 +203,7 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -195,7 +203,7 @@ public class HlsSessionWorker : IHlsSessionWorker
_logger.LogError("Transcode folder is NOT empty!");
}
Touch();
Touch(Option<string>.None);
_transcodedUntil = DateTimeOffset.Now;
PlaylistStart = _transcodedUntil;
_channelStart = _transcodedUntil;
@ -661,6 +669,8 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -661,6 +669,8 @@ public class HlsSessionWorker : IHlsSessionWorker
private void DeleteOldSegments(TrimPlaylistResult trimResult)
{
var generatedAtHash = new System.Collections.Generic.HashSet<long>();
// delete old segments
var allSegments = Directory.GetFiles(_workingDirectory, "live*.ts")
.Append(Directory.GetFiles(_workingDirectory, "live*.mp4"))
@ -677,6 +687,7 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -677,6 +687,7 @@ public class HlsSessionWorker : IHlsSessionWorker
{
generatedAt = 0;
}
generatedAtHash.Add(generatedAt);
return new Segment(file, sequenceNumber, generatedAt);
})
.ToList();
@ -685,7 +696,8 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -685,7 +696,8 @@ public class HlsSessionWorker : IHlsSessionWorker
.Map(file =>
{
string fileName = Path.GetFileName(file);
return long.TryParse(fileName.Split('_')[0], out long generatedAt)
// only consider deleting inits that have no segments left on disk
return long.TryParse(fileName.Split('_')[0], out long generatedAt) && !generatedAtHash.Contains(generatedAt)
? new Segment(file, 0, generatedAt)
: Option<Segment>.None;
})
@ -702,15 +714,20 @@ public class HlsSessionWorker : IHlsSessionWorker @@ -702,15 +714,20 @@ public class HlsSessionWorker : IHlsSessionWorker
// trimResult.Sequence);
}
if (allInits.Count > 0 && allSegments.Count > 0)
DateTimeOffset toKeep = DateTimeOffset.Now.AddMinutes(-10);
foreach (var init in allInits)
{
long minKeep = allSegments.Except(toDelete).Map(s => s.GeneratedAt).Min();
Option<Segment> maybeMinKeepInit =
allInits.OrderByDescending(i => i.GeneratedAt).Find(i => i.GeneratedAt <= minKeep);
foreach (var minKeepInit in maybeMinKeepInit)
string fileName = Path.GetFileName(init.File) ?? string.Empty;
if (!_initTouches.TryGetValue(fileName, out DateTimeOffset touchedAt))
{
continue;
}
if (touchedAt < toKeep)
{
// _logger.LogDebug("Deleting HLS inits less than {GeneratedAt}", minKeepInit.GeneratedAt);
toDelete.AddRange(allInits.Where(i => i.GeneratedAt < minKeepInit.GeneratedAt));
toDelete.Add(init);
_initTouches.TryRemove(fileName, out _);
}
}

4
ErsatzTV.Core/FFmpeg/FFmpegSegmenterService.cs

@ -72,11 +72,11 @@ public class FFmpegSegmenterService(ILogger<FFmpegSegmenterService> logger) : IF @@ -72,11 +72,11 @@ public class FFmpegSegmenterService(ILogger<FFmpegSegmenterService> logger) : IF
return false;
}
public void TouchChannel(string channelNumber)
public void TouchChannel(string channelNumber, string fileName)
{
if (_sessionWorkers.TryGetValue(channelNumber, out IHlsSessionWorker worker))
{
worker?.Touch();
worker?.Touch(fileName);
}
}

2
ErsatzTV.Core/Interfaces/FFmpeg/IFFmpegSegmenterService.cs

@ -10,6 +10,6 @@ public interface IFFmpegSegmenterService @@ -10,6 +10,6 @@ public interface IFFmpegSegmenterService
void RemoveWorker(string channelNumber, out IHlsSessionWorker inactiveWorker);
bool IsActive(string channelNumber);
Task<bool> StopChannel(string channelNumber, CancellationToken cancellationToken);
void TouchChannel(string channelNumber);
void TouchChannel(string channelNumber, string fileName);
void PlayoutUpdated(string channelNumber);
}

2
ErsatzTV.Core/Interfaces/FFmpeg/IHlsSessionWorker.cs

@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.FFmpeg; @@ -5,7 +5,7 @@ namespace ErsatzTV.Core.Interfaces.FFmpeg;
public interface IHlsSessionWorker : IDisposable
{
Task Cancel(CancellationToken cancellationToken);
void Touch();
void Touch(Option<string> fileName);
Task<Option<TrimPlaylistResult>> TrimPlaylist(DateTimeOffset filterBefore, CancellationToken cancellationToken);
void PlayoutUpdated();
HlsSessionModel GetModel();

5
ErsatzTV.FFmpeg/Pipeline/PipelineBuilderBase.cs

@ -170,6 +170,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder @@ -170,6 +170,11 @@ public abstract class PipelineBuilderBase : IPipelineBuilder
pipelineSteps.Add(new OutputFormatMpegTs(false));
pipelineSteps.Add(new PipeProtocol());
// if (ffmpegState.SaveReport)
// {
// pipelineSteps.Add(new FFReportVariable(_reportsFolder, concatInputFile));
// }
return new FFmpegPipeline(pipelineSteps, false);
}

2
ErsatzTV/Services/FFmpegWorkerService.cs

@ -46,7 +46,7 @@ public class FFmpegWorkerService : BackgroundService @@ -46,7 +46,7 @@ public class FFmpegWorkerService : BackgroundService
case TouchFFmpegSession touchFFmpegSession:
foreach (DirectoryInfo parent in Optional(Directory.GetParent(touchFFmpegSession.Path)))
{
_ffmpegSegmenterService.TouchChannel(parent.Name);
_ffmpegSegmenterService.TouchChannel(parent.Name, Path.GetFileName(touchFFmpegSession.Path));
}
break;

Loading…
Cancel
Save