Restructure the code to make it more testable

pull/6600/head
cvium 3 years ago
parent be233b49b6
commit fa38b741ce

@ -49,13 +49,12 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
/// <inheritdoc /> /// <inheritdoc />
public string CreateMainPlaylist(CreateMainPlaylistRequest request) public string CreateMainPlaylist(CreateMainPlaylistRequest request)
{ {
IReadOnlyList<double> segments = Array.Empty<double>(); IReadOnlyList<double> segments;
if (IsExtractionAllowedForFile(request.FilePath)) if (TryExtractKeyframes(request.FilePath, out var keyframeData))
{ {
segments = ComputeSegments(request.FilePath, request.DesiredSegmentLengthMs); segments = ComputeSegments(keyframeData, request.DesiredSegmentLengthMs);
} }
else
if (segments.Count == 0)
{ {
segments = ComputeEqualLengthSegments(request.DesiredSegmentLengthMs, request.TotalRuntimeTicks); segments = ComputeEqualLengthSegments(request.DesiredSegmentLengthMs, request.TotalRuntimeTicks);
} }
@ -115,9 +114,14 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
return builder.ToString(); return builder.ToString();
} }
private IReadOnlyList<double> ComputeSegments(string filePath, int desiredSegmentLengthMs) private bool TryExtractKeyframes(string filePath, [NotNullWhen(true)] out KeyframeData? keyframeData)
{ {
KeyframeData keyframeData; keyframeData = null;
if (!IsExtractionAllowedForFile(filePath, _serverConfigurationManager.GetEncodingOptions().AllowAutomaticKeyframeExtractionForExtensions))
{
return false;
}
var cachePath = GetCachePath(filePath); var cachePath = GetCachePath(filePath);
if (TryReadFromCache(cachePath, out var cachedResult)) if (TryReadFromCache(cachePath, out var cachedResult))
{ {
@ -132,31 +136,13 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
catch (Exception ex) catch (Exception ex)
{ {
_logger.LogError(ex, "Keyframe extraction failed for path {FilePath}", filePath); _logger.LogError(ex, "Keyframe extraction failed for path {FilePath}", filePath);
return Array.Empty<double>(); return false;
} }
CacheResult(cachePath, keyframeData); CacheResult(cachePath, keyframeData);
} }
long lastKeyframe = 0; return keyframeData.KeyframeTicks.Count > 0;
var result = new List<double>();
// Scale the segment length to ticks to match the keyframes
var desiredSegmentLengthTicks = TimeSpan.FromMilliseconds(desiredSegmentLengthMs).Ticks;
var desiredCutTime = desiredSegmentLengthTicks;
for (var j = 0; j < keyframeData.KeyframeTicks.Count; j++)
{
var keyframe = keyframeData.KeyframeTicks[j];
if (keyframe >= desiredCutTime)
{
var currentSegmentLength = keyframe - lastKeyframe;
result.Add(TimeSpan.FromTicks(currentSegmentLength).TotalSeconds);
lastKeyframe = keyframe;
desiredCutTime += desiredSegmentLengthTicks;
}
}
result.Add(TimeSpan.FromTicks(keyframeData.TotalDuration - lastKeyframe).TotalSeconds);
return result;
} }
private void CacheResult(string cachePath, KeyframeData keyframeData) private void CacheResult(string cachePath, KeyframeData keyframeData)
@ -188,11 +174,10 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
return false; return false;
} }
private bool IsExtractionAllowedForFile(ReadOnlySpan<char> filePath) internal static bool IsExtractionAllowedForFile(ReadOnlySpan<char> filePath, string[] allowedExtensions)
{ {
// Remove the leading dot // Remove the leading dot
var extension = Path.GetExtension(filePath)[1..]; var extension = Path.GetExtension(filePath)[1..];
var allowedExtensions = _serverConfigurationManager.GetEncodingOptions().AllowAutomaticKeyframeExtractionForExtensions;
for (var i = 0; i < allowedExtensions.Length; i++) for (var i = 0; i < allowedExtensions.Length; i++)
{ {
var allowedExtension = allowedExtensions[i]; var allowedExtension = allowedExtensions[i];
@ -205,7 +190,30 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
return false; return false;
} }
private static double[] ComputeEqualLengthSegments(long desiredSegmentLengthMs, long totalRuntimeTicks) internal static IReadOnlyList<double> ComputeSegments(KeyframeData keyframeData, int desiredSegmentLengthMs)
{
long lastKeyframe = 0;
var result = new List<double>();
// Scale the segment length to ticks to match the keyframes
var desiredSegmentLengthTicks = TimeSpan.FromMilliseconds(desiredSegmentLengthMs).Ticks;
var desiredCutTime = desiredSegmentLengthTicks;
for (var j = 0; j < keyframeData.KeyframeTicks.Count; j++)
{
var keyframe = keyframeData.KeyframeTicks[j];
if (keyframe >= desiredCutTime)
{
var currentSegmentLength = keyframe - lastKeyframe;
result.Add(TimeSpan.FromTicks(currentSegmentLength).TotalSeconds);
lastKeyframe = keyframe;
desiredCutTime += desiredSegmentLengthTicks;
}
}
result.Add(TimeSpan.FromTicks(keyframeData.TotalDuration - lastKeyframe).TotalSeconds);
return result;
}
internal static double[] ComputeEqualLengthSegments(long desiredSegmentLengthMs, long totalRuntimeTicks)
{ {
var segmentLengthTicks = TimeSpan.FromMilliseconds(desiredSegmentLengthMs).Ticks; var segmentLengthTicks = TimeSpan.FromMilliseconds(desiredSegmentLengthMs).Ticks;
var wholeSegments = totalRuntimeTicks / segmentLengthTicks; var wholeSegments = totalRuntimeTicks / segmentLengthTicks;

Loading…
Cancel
Save