consolidate streaming modes (#2544)

* consolidate segmenters

* let old segmenter mode query params continue to work
This commit is contained in:
Jason Dove
2025-10-18 10:54:30 -05:00
committed by GitHub
parent ba5a027525
commit ffe15629cb
30 changed files with 13637 additions and 853 deletions
@@ -119,7 +119,7 @@ public class TroubleshootController(
string[] segmentFiles = streamingMode switch
{
StreamingMode.HttpLiveStreamingSegmenterFmp4 => Directory.GetFiles(
StreamingMode.HttpLiveStreamingSegmenter => Directory.GetFiles(
FileSystemLayout.TranscodeTroubleshootingFolder,
"*.m4s"),
_ => Directory.GetFiles(FileSystemLayout.TranscodeTroubleshootingFolder, "*.ts")
+1 -30
View File
@@ -10,7 +10,6 @@ using ErsatzTV.Application.Subtitles.Queries;
using ErsatzTV.Core;
using ErsatzTV.Core.Domain;
using ErsatzTV.Core.FFmpeg;
using ErsatzTV.Core.Interfaces.FFmpeg;
using ErsatzTV.Core.Interfaces.Streaming;
using ErsatzTV.Extensions;
using Flurl;
@@ -23,18 +22,15 @@ namespace ErsatzTV.Controllers;
[ApiExplorerSettings(IgnoreApi = true)]
public class InternalController : StreamingControllerBase
{
private readonly IFFmpegSegmenterService _ffmpegSegmenterService;
private readonly ILogger<InternalController> _logger;
private readonly IMediator _mediator;
public InternalController(
IFFmpegSegmenterService ffmpegSegmenterService,
IGraphicsEngine graphicsEngine,
IMediator mediator,
ILogger<InternalController> logger)
: base(graphicsEngine, logger)
{
_ffmpegSegmenterService = ffmpegSegmenterService;
_mediator = mediator;
_logger = logger;
}
@@ -46,19 +42,7 @@ public class InternalController : StreamingControllerBase
.ToActionResult();
[HttpGet("ffmpeg/stream/{channelNumber}")]
public async Task<IActionResult> GetStream(
string channelNumber,
[FromQuery]
string mode = "mixed")
{
switch (mode)
{
case "segmenter-v2":
return await GetSegmenterV2Stream(channelNumber);
default:
return await GetTsLegacyStream(channelNumber);
}
}
public Task<IActionResult> GetStream(string channelNumber) => GetTsLegacyStream(channelNumber);
[HttpGet("ffmpeg/remote-stream/{remoteStreamId}")]
public async Task<IActionResult> GetRemoteStream(int remoteStreamId, CancellationToken cancellationToken)
@@ -262,19 +246,6 @@ public class InternalController : StreamingControllerBase
return new NotFoundResult();
}
private async Task<IActionResult> GetSegmenterV2Stream(string channelNumber)
{
if (_ffmpegSegmenterService.TryGetWorker(channelNumber, out IHlsSessionWorker worker) &&
worker is HlsSessionWorkerV2 v2)
{
Either<BaseError, PlayoutItemProcessModel> result = await v2.GetNextPlayoutItemProcess();
return GetProcessResponse(result, channelNumber, StreamingMode.HttpLiveStreamingSegmenterV2);
}
_logger.LogWarning("Unable to locate session worker for channel {Channel}", channelNumber);
return new NotFoundResult();
}
private async Task<IActionResult> GetTsLegacyStream(string channelNumber)
{
var request = new GetPlayoutItemProcessByChannelNumber(
+4 -37
View File
@@ -208,12 +208,6 @@ public class IptvController : StreamingControllerBase
case StreamingMode.HttpLiveStreamingSegmenter:
mode = "segmenter";
break;
case StreamingMode.HttpLiveStreamingSegmenterFmp4:
mode = "segmenter-fmp4";
break;
case StreamingMode.HttpLiveStreamingSegmenterV2:
mode = "segmenter-v2";
break;
default:
return Redirect($"~/iptv/channel/{channelNumber}.ts{AccessTokenQuery()}");
}
@@ -223,15 +217,15 @@ public class IptvController : StreamingControllerBase
switch (mode)
{
case "segmenter":
case "segmenter-fmp4":
case "segmenter-v2":
case "segmenter-fmp4":
_logger.LogDebug(
"Maybe starting ffmpeg session for channel {Channel}, mode {Mode}",
channelNumber,
mode);
var request = new StartFFmpegSession(channelNumber, mode, Request.Scheme, Request.Host.ToString());
Either<BaseError, Unit> result = await _mediator.Send(request);
string multiVariantPlaylist = await GetMultiVariantPlaylist(channelNumber, mode);
string multiVariantPlaylist = await GetMultiVariantPlaylist(channelNumber);
return result.Match<IActionResult>(
_ =>
{
@@ -291,37 +285,10 @@ public class IptvController : StreamingControllerBase
public async Task<IActionResult> GetStream(string channelNumber) =>
await GetHlsDirectStream(channelNumber);
private async Task<string> GetMultiVariantPlaylist(string channelNumber, string mode)
private async Task<string> GetMultiVariantPlaylist(string channelNumber)
{
string file = mode switch
{
// this serves the unmodified playlist from disk
"segmenter-v2" => "live.m3u8",
_ => "hls.m3u8"
};
var variantPlaylist =
$"{Request.Scheme}://{Request.Host}/iptv/session/{channelNumber}/{file}{AccessTokenQuery()}";
try
{
if (mode == "segmenter-v2")
{
string fileName = Path.Combine(FileSystemLayout.TranscodeFolder, channelNumber, "playlist.m3u8");
if (System.IO.File.Exists(fileName))
{
string text = await System.IO.File.ReadAllTextAsync(fileName, Encoding.UTF8);
return text.Replace("live.m3u8", variantPlaylist);
}
}
}
catch (Exception ex)
{
_logger.LogWarning(
ex,
"Failed to return ffmpeg multi-variant playlist; falling back to generated playlist");
}
$"{Request.Scheme}://{Request.Host}/iptv/session/{channelNumber}/hls.m3u8{AccessTokenQuery()}";
Option<ChannelStreamingSpecsViewModel> maybeStreamingSpecs =
await _mediator.Send(new GetChannelStreamingSpecs(channelNumber));
@@ -77,13 +77,7 @@ public abstract class StreamingControllerBase(IGraphicsEngine graphicsEngine, IL
pipe.Writer,
TaskScheduler.Default);
string contentType = mode switch
{
StreamingMode.HttpLiveStreamingSegmenterV2 => "video/x-matroska",
_ => "video/mp2t"
};
return new FileStreamResult(pipe.Reader.AsStream(), contentType);
return new FileStreamResult(pipe.Reader.AsStream(), "video/mp2t");
}
// this will never happen