Browse Source

use graphics engine with segmenter v2 (#2294)

pull/2295/head
Jason Dove 6 days ago committed by GitHub
parent
commit
1bf5b9567b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 2
      ErsatzTV.Application/Streaming/HlsSessionWorkerV2.cs
  2. 67
      ErsatzTV/Controllers/InternalController.cs
  3. 2
      ErsatzTV/Pages/ChannelEditor.razor

2
ErsatzTV.Application/Streaming/HlsSessionWorkerV2.cs

@ -24,7 +24,7 @@ public class HlsSessionWorkerV2 : IHlsSessionWorker
private readonly IMediator _mediator; private readonly IMediator _mediator;
private readonly string _scheme; private readonly string _scheme;
private readonly SemaphoreSlim _slim = new(1, 1); private readonly SemaphoreSlim _slim = new(1, 1);
private readonly object _sync = new(); private readonly Lock _sync = new();
private readonly Option<int> _targetFramerate; private readonly Option<int> _targetFramerate;
private CancellationTokenSource _cancellationTokenSource; private CancellationTokenSource _cancellationTokenSource;
private string _channelNumber; private string _channelNumber;

67
ErsatzTV/Controllers/InternalController.cs

@ -1,4 +1,6 @@
using System.Diagnostics; using System.Diagnostics;
using System.IO.Pipelines;
using System.Text;
using CliWrap; using CliWrap;
using ErsatzTV.Application.Emby; using ErsatzTV.Application.Emby;
using ErsatzTV.Application.Jellyfin; using ErsatzTV.Application.Jellyfin;
@ -9,6 +11,7 @@ using ErsatzTV.Application.Subtitles.Queries;
using ErsatzTV.Core; using ErsatzTV.Core;
using ErsatzTV.Core.FFmpeg; using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg; using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Streaming;
using ErsatzTV.Extensions; using ErsatzTV.Extensions;
using Flurl; using Flurl;
using MediatR; using MediatR;
@ -21,15 +24,18 @@ namespace ErsatzTV.Controllers;
public class InternalController : ControllerBase public class InternalController : ControllerBase
{ {
private readonly IFFmpegSegmenterService _ffmpegSegmenterService; private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
private readonly IGraphicsEngine _graphicsEngine;
private readonly ILogger<InternalController> _logger; private readonly ILogger<InternalController> _logger;
private readonly IMediator _mediator; private readonly IMediator _mediator;
public InternalController( public InternalController(
IFFmpegSegmenterService ffmpegSegmenterService, IFFmpegSegmenterService ffmpegSegmenterService,
IGraphicsEngine graphicsEngine,
IMediator mediator, IMediator mediator,
ILogger<InternalController> logger) ILogger<InternalController> logger)
{ {
_ffmpegSegmenterService = ffmpegSegmenterService; _ffmpegSegmenterService = ffmpegSegmenterService;
_graphicsEngine = graphicsEngine;
_mediator = mediator; _mediator = mediator;
_logger = logger; _logger = logger;
} }
@ -298,38 +304,55 @@ public class InternalController : ControllerBase
foreach (PlayoutItemProcessModel processModel in result.RightToSeq()) foreach (PlayoutItemProcessModel processModel in result.RightToSeq())
{ {
Command command = processModel.Process; // for process counter
var ffmpegProcess = new FFmpegProcess();
_logger.LogDebug("ffmpeg arguments {FFmpegArguments}", command.Arguments); Command process = processModel.Process;
var process = new FFmpegProcess _logger.LogDebug("ffmpeg arguments {FFmpegArguments}", process.Arguments);
{
StartInfo = new ProcessStartInfo var cts = new CancellationTokenSource();
HttpContext.Response.OnCompleted(
async () =>
{ {
FileName = command.TargetFilePath, ffmpegProcess.Dispose();
Arguments = command.Arguments, await cts.CancelAsync();
RedirectStandardOutput = true, cts.Dispose();
RedirectStandardError = false, });
UseShellExecute = false,
CreateNoWindow = true
}
};
HttpContext.Response.RegisterForDispose(process); using var linkedCts = CancellationTokenSource.CreateLinkedTokenSource(
cts.Token,
HttpContext.RequestAborted);
foreach ((string key, string value) in command.EnvironmentVariables) var pipe = new Pipe();
var stdErrBuffer = new StringBuilder();
var processWithPipe = process;
foreach (var graphicsEngineContext in processModel.GraphicsEngineContext)
{ {
process.StartInfo.Environment[key] = value; var gePipe = new Pipe();
processWithPipe = process.WithStandardInputPipe(PipeSource.FromStream(gePipe.Reader.AsStream()));
// fire and forget graphics engine task
_ = _graphicsEngine.Run(
graphicsEngineContext,
gePipe.Writer,
linkedCts.Token);
} }
var contentType = "video/mp2t"; _ = processWithPipe
if (mode.Equals("hls-direct", StringComparison.OrdinalIgnoreCase)) .WithStandardOutputPipe(PipeTarget.ToStream(pipe.Writer.AsStream()))
.WithStandardErrorPipe(PipeTarget.ToStringBuilder(stdErrBuffer))
.WithValidation(CommandResultValidation.None)
.ExecuteAsync(linkedCts.Token);
var contentType = mode switch
{ {
contentType = "video/mp4"; "segmenter-v2" => "video/x-matroska",
} _ => "video/mp2t"
};
process.Start(); return new FileStreamResult(pipe.Reader.AsStream(), contentType);
return new FileStreamResult(process.StandardOutput.BaseStream, contentType);
} }
// this will never happen // this will never happen

2
ErsatzTV/Pages/ChannelEditor.razor

@ -94,7 +94,7 @@
</div> </div>
<MudSelect @bind-Value="_model.StreamingMode" For="@(() => _model.StreamingMode)"> <MudSelect @bind-Value="_model.StreamingMode" For="@(() => _model.StreamingMode)">
<MudSelectItem Value="@(StreamingMode.TransportStreamHybrid)">MPEG-TS</MudSelectItem> <MudSelectItem Value="@(StreamingMode.TransportStreamHybrid)">MPEG-TS</MudSelectItem>
@* <MudSelectItem Value="@(StreamingMode.TransportStream)">MPEG-TS (Legacy)</MudSelectItem> *@ <MudSelectItem Value="@(StreamingMode.TransportStream)">MPEG-TS (Legacy)</MudSelectItem>
<MudSelectItem Value="@(StreamingMode.HttpLiveStreamingDirect)">HLS Direct</MudSelectItem> <MudSelectItem Value="@(StreamingMode.HttpLiveStreamingDirect)">HLS Direct</MudSelectItem>
<MudSelectItem Value="@(StreamingMode.HttpLiveStreamingSegmenter)">HLS Segmenter</MudSelectItem> <MudSelectItem Value="@(StreamingMode.HttpLiveStreamingSegmenter)">HLS Segmenter</MudSelectItem>
<MudSelectItem Value="@(StreamingMode.HttpLiveStreamingSegmenterV2)">HLS Segmenter V2</MudSelectItem> <MudSelectItem Value="@(StreamingMode.HttpLiveStreamingSegmenterV2)">HLS Segmenter V2</MudSelectItem>

Loading…
Cancel
Save