fix subsequent hls session work ahead (#1419)
This commit is contained in:
+1
-1
@@ -28,7 +28,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
|
|||||||
- Fix adding alternate schedule
|
- Fix adding alternate schedule
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
- Optimize transcoding session to only work ahead (at max speed) for 2 minutes before throttling to realtime
|
- Optimize transcoding session to only work ahead (at max speed) for 3 minutes before throttling to realtime
|
||||||
- This should *greatly* reduce cpu/gpu use when joining a channel, particularly with long content
|
- This should *greatly* reduce cpu/gpu use when joining a channel, particularly with long content
|
||||||
- Allow manually editing (typing) schedule item fixed start time
|
- Allow manually editing (typing) schedule item fixed start time
|
||||||
- Use different control for editing schedule item duration, and allow 24-hour duration
|
- Use different control for editing schedule item duration, and allow 24-hour duration
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ namespace ErsatzTV.Application.Streaming;
|
|||||||
|
|
||||||
public class HlsSessionWorker : IHlsSessionWorker
|
public class HlsSessionWorker : IHlsSessionWorker
|
||||||
{
|
{
|
||||||
|
public static readonly TimeSpan WorkAheadDuration = TimeSpan.FromMinutes(3);
|
||||||
|
|
||||||
private static readonly SemaphoreSlim Slim = new(1, 1);
|
private static readonly SemaphoreSlim Slim = new(1, 1);
|
||||||
private static int _workAheadCount;
|
private static int _workAheadCount;
|
||||||
private readonly IMediator _mediator;
|
private readonly IMediator _mediator;
|
||||||
@@ -269,17 +271,27 @@ public class HlsSessionWorker : IHlsSessionWorker
|
|||||||
{
|
{
|
||||||
Interlocked.Increment(ref _workAheadCount);
|
Interlocked.Increment(ref _workAheadCount);
|
||||||
_logger.LogInformation("HLS segmenter will work ahead for channel {Channel}", _channelNumber);
|
_logger.LogInformation("HLS segmenter will work ahead for channel {Channel}", _channelNumber);
|
||||||
|
|
||||||
|
HlsSessionState nextState = _state switch
|
||||||
|
{
|
||||||
|
HlsSessionState.SeekAndRealtime => HlsSessionState.SeekAndWorkAhead,
|
||||||
|
HlsSessionState.ZeroAndRealtime => HlsSessionState.ZeroAndWorkAhead,
|
||||||
|
_ => _state
|
||||||
|
};
|
||||||
|
|
||||||
|
if (nextState != _state)
|
||||||
|
{
|
||||||
|
_logger.LogDebug("HLS session state accelerating {Last} => {Next}", _state, nextState);
|
||||||
|
_state = nextState;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_logger.LogInformation(
|
_logger.LogInformation(
|
||||||
"HLS segmenter will NOT work ahead for channel {Channel}",
|
"HLS segmenter will NOT work ahead for channel {Channel}",
|
||||||
_channelNumber);
|
_channelNumber);
|
||||||
}
|
|
||||||
|
|
||||||
// throttle to realtime if needed
|
// throttle to realtime if needed
|
||||||
if (realtime)
|
|
||||||
{
|
|
||||||
HlsSessionState nextState = _state switch
|
HlsSessionState nextState = _state switch
|
||||||
{
|
{
|
||||||
HlsSessionState.SeekAndWorkAhead => HlsSessionState.SeekAndRealtime,
|
HlsSessionState.SeekAndWorkAhead => HlsSessionState.SeekAndRealtime,
|
||||||
|
|||||||
+2
-2
@@ -182,9 +182,9 @@ public class GetPlayoutItemProcessByChannelNumberHandler : FFmpegProcessHandler<
|
|||||||
|
|
||||||
// _logger.LogDebug("PRE Start: {Start}, Finish {Finish}", start, finish);
|
// _logger.LogDebug("PRE Start: {Start}, Finish {Finish}", start, finish);
|
||||||
// _logger.LogDebug("PRE in: {In}, out: {Out}", inPoint, outPoint);
|
// _logger.LogDebug("PRE in: {In}, out: {Out}", inPoint, outPoint);
|
||||||
if (!request.HlsRealtime && duration > TimeSpan.FromMinutes(2))
|
if (!request.HlsRealtime && duration > HlsSessionWorker.WorkAheadDuration)
|
||||||
{
|
{
|
||||||
finish = effectiveNow + TimeSpan.FromMinutes(2);
|
finish = effectiveNow + HlsSessionWorker.WorkAheadDuration;
|
||||||
outPoint = finish - start + inPoint;
|
outPoint = finish - start + inPoint;
|
||||||
isComplete = false;
|
isComplete = false;
|
||||||
|
|
||||||
|
|||||||
@@ -138,9 +138,9 @@ public class ArtworkController : ControllerBase
|
|||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
#if DEBUG_NO_SYNC
|
#if DEBUG_NO_SYNC
|
||||||
|
await Task.CompletedTask;
|
||||||
return NotFound();
|
return NotFound();
|
||||||
#endif
|
#else
|
||||||
|
|
||||||
Either<BaseError, PlexConnectionParametersViewModel> connectionParameters =
|
Either<BaseError, PlexConnectionParametersViewModel> connectionParameters =
|
||||||
await _mediator.Send(new GetPlexConnectionParameters(plexMediaSourceId), cancellationToken);
|
await _mediator.Send(new GetPlexConnectionParameters(plexMediaSourceId), cancellationToken);
|
||||||
|
|
||||||
@@ -165,14 +165,15 @@ public class ArtworkController : ControllerBase
|
|||||||
stream,
|
stream,
|
||||||
response.Content.Headers.ContentType?.MediaType ?? "image/jpeg");
|
response.Content.Headers.ContentType?.MediaType ?? "image/jpeg");
|
||||||
});
|
});
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IActionResult> GetJellyfinArtwork(string path, CancellationToken cancellationToken)
|
private async Task<IActionResult> GetJellyfinArtwork(string path, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
#if DEBUG_NO_SYNC
|
#if DEBUG_NO_SYNC
|
||||||
|
await Task.CompletedTask;
|
||||||
return NotFound();
|
return NotFound();
|
||||||
#endif
|
#else
|
||||||
|
|
||||||
Either<BaseError, JellyfinConnectionParametersViewModel> connectionParameters =
|
Either<BaseError, JellyfinConnectionParametersViewModel> connectionParameters =
|
||||||
await _mediator.Send(new GetJellyfinConnectionParameters(), cancellationToken);
|
await _mediator.Send(new GetJellyfinConnectionParameters(), cancellationToken);
|
||||||
|
|
||||||
@@ -196,14 +197,15 @@ public class ArtworkController : ControllerBase
|
|||||||
stream,
|
stream,
|
||||||
response.Content.Headers.ContentType?.MediaType ?? "image/jpeg");
|
response.Content.Headers.ContentType?.MediaType ?? "image/jpeg");
|
||||||
});
|
});
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<IActionResult> GetEmbyArtwork(string path, CancellationToken cancellationToken)
|
private async Task<IActionResult> GetEmbyArtwork(string path, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
#if DEBUG_NO_SYNC
|
#if DEBUG_NO_SYNC
|
||||||
|
await Task.CompletedTask;
|
||||||
return NotFound();
|
return NotFound();
|
||||||
#endif
|
#else
|
||||||
|
|
||||||
Either<BaseError, EmbyConnectionParametersViewModel> connectionParameters =
|
Either<BaseError, EmbyConnectionParametersViewModel> connectionParameters =
|
||||||
await _mediator.Send(new GetEmbyConnectionParameters(), cancellationToken);
|
await _mediator.Send(new GetEmbyConnectionParameters(), cancellationToken);
|
||||||
|
|
||||||
@@ -227,5 +229,6 @@ public class ArtworkController : ControllerBase
|
|||||||
stream,
|
stream,
|
||||||
response.Content.Headers.ContentType?.MediaType ?? "image/jpeg");
|
response.Content.Headers.ContentType?.MediaType ?? "image/jpeg");
|
||||||
});
|
});
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user