Fixes
This commit is contained in:
parent
35c0801d6c
commit
be233b49b6
12
Jellyfin.sln
12
Jellyfin.sln
|
@ -223,10 +223,6 @@ Global
|
||||||
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU
|
{42816EA8-4511-4CBF-A9C7-7791D5DDDAE6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
|
||||||
{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
|
||||||
{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
|
||||||
{25E40B0B-7C89-4230-8911-CBBBCE83FC5B}.Release|Any CPU.Build.0 = Release|Any CPU
|
|
||||||
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{3ADBCD8C-C0F2-4956-8FDC-35D686B74CF9}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
@ -249,12 +245,12 @@ Global
|
||||||
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
{332A5C7A-F907-47CA-910E-BE6F7371B9E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
{06535CA1-4097-4360-85EB-5FB875D53239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{06535CA1-4097-4360-85EB-5FB875D53239}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{06535CA1-4097-4360-85EB-5FB875D53239}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{06535CA1-4097-4360-85EB-5FB875D53239}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{06535CA1-4097-4360-85EB-5FB875D53239}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{06535CA1-4097-4360-85EB-5FB875D53239}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{06535CA1-4097-4360-85EB-5FB875D53239}.Release|Any CPU.Build.0 = Release|Any CPU
|
{06535CA1-4097-4360-85EB-5FB875D53239}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Release|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Release|Any CPU.Build.0 = Release|Any CPU
|
{DA9FD356-4894-4830-B208-D6BCE3E65B11}.Release|Any CPU.Build.0 = Debug|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
|
||||||
private readonly IMediaEncoder _mediaEncoder;
|
private readonly IMediaEncoder _mediaEncoder;
|
||||||
private readonly IApplicationPaths _applicationPaths;
|
private readonly IApplicationPaths _applicationPaths;
|
||||||
private readonly KeyframeExtractor _keyframeExtractor;
|
private readonly KeyframeExtractor _keyframeExtractor;
|
||||||
|
private readonly ILogger<DynamicHlsPlaylistGenerator> _logger;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Initializes a new instance of the <see cref="DynamicHlsPlaylistGenerator"/> class.
|
/// Initializes a new instance of the <see cref="DynamicHlsPlaylistGenerator"/> class.
|
||||||
|
@ -40,6 +41,7 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
|
||||||
_mediaEncoder = mediaEncoder;
|
_mediaEncoder = mediaEncoder;
|
||||||
_applicationPaths = applicationPaths;
|
_applicationPaths = applicationPaths;
|
||||||
_keyframeExtractor = new KeyframeExtractor(loggerFactory.CreateLogger<KeyframeExtractor>());
|
_keyframeExtractor = new KeyframeExtractor(loggerFactory.CreateLogger<KeyframeExtractor>());
|
||||||
|
_logger = loggerFactory.CreateLogger<DynamicHlsPlaylistGenerator>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private string KeyframeCachePath => Path.Combine(_applicationPaths.DataPath, "keyframes");
|
private string KeyframeCachePath => Path.Combine(_applicationPaths.DataPath, "keyframes");
|
||||||
|
@ -47,12 +49,13 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public string CreateMainPlaylist(CreateMainPlaylistRequest request)
|
public string CreateMainPlaylist(CreateMainPlaylistRequest request)
|
||||||
{
|
{
|
||||||
IReadOnlyList<double> segments;
|
IReadOnlyList<double> segments = Array.Empty<double>();
|
||||||
if (IsExtractionAllowed(request.FilePath))
|
if (IsExtractionAllowedForFile(request.FilePath))
|
||||||
{
|
{
|
||||||
segments = ComputeSegments(request.FilePath, request.DesiredSegmentLengthMs);
|
segments = ComputeSegments(request.FilePath, request.DesiredSegmentLengthMs);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
if (segments.Count == 0)
|
||||||
{
|
{
|
||||||
segments = ComputeEqualLengthSegments(request.DesiredSegmentLengthMs, request.TotalRuntimeTicks);
|
segments = ComputeEqualLengthSegments(request.DesiredSegmentLengthMs, request.TotalRuntimeTicks);
|
||||||
}
|
}
|
||||||
|
@ -92,7 +95,7 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
|
||||||
foreach (var length in segments)
|
foreach (var length in segments)
|
||||||
{
|
{
|
||||||
builder.Append("#EXTINF:")
|
builder.Append("#EXTINF:")
|
||||||
.Append(length.ToString("0.0000", CultureInfo.InvariantCulture))
|
.Append(length.ToString("0.000000", CultureInfo.InvariantCulture))
|
||||||
.AppendLine(", nodesc")
|
.AppendLine(", nodesc")
|
||||||
.Append(request.EndpointPrefix)
|
.Append(request.EndpointPrefix)
|
||||||
.Append(index++)
|
.Append(index++)
|
||||||
|
@ -122,7 +125,16 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
keyframeData = _keyframeExtractor.GetKeyframeData(filePath, _mediaEncoder.ProbePath, string.Empty);
|
try
|
||||||
|
{
|
||||||
|
keyframeData = _keyframeExtractor.GetKeyframeData(filePath, _mediaEncoder.ProbePath, string.Empty);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "Keyframe extraction failed for path {FilePath}", filePath);
|
||||||
|
return Array.Empty<double>();
|
||||||
|
}
|
||||||
|
|
||||||
CacheResult(cachePath, keyframeData);
|
CacheResult(cachePath, keyframeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -176,7 +188,7 @@ namespace Jellyfin.MediaEncoding.Hls.Playlist
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsExtractionAllowed(ReadOnlySpan<char> filePath)
|
private bool IsExtractionAllowedForFile(ReadOnlySpan<char> filePath)
|
||||||
{
|
{
|
||||||
// Remove the leading dot
|
// Remove the leading dot
|
||||||
var extension = Path.GetExtension(filePath)[1..];
|
var extension = Path.GetExtension(filePath)[1..];
|
||||||
|
|
|
@ -32,25 +32,38 @@ namespace Jellyfin.MediaEncoding.Keyframes
|
||||||
/// <returns>An instance of <see cref="KeyframeData"/>.</returns>
|
/// <returns>An instance of <see cref="KeyframeData"/>.</returns>
|
||||||
public KeyframeData GetKeyframeData(string filePath, string ffProbePath, string ffToolPath)
|
public KeyframeData GetKeyframeData(string filePath, string ffProbePath, string ffToolPath)
|
||||||
{
|
{
|
||||||
var extension = Path.GetExtension(filePath);
|
var extension = Path.GetExtension(filePath.AsSpan());
|
||||||
if (string.Equals(extension, ".mkv", StringComparison.OrdinalIgnoreCase))
|
if (extension.Equals(".mkv", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return MatroskaKeyframeExtractor.GetKeyframeData(filePath);
|
return MatroskaKeyframeExtractor.GetKeyframeData(filePath);
|
||||||
}
|
}
|
||||||
catch (InvalidOperationException ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
_logger.LogError(ex, "{MatroskaKeyframeExtractor} failed to extract keyframes", nameof(MatroskaKeyframeExtractor));
|
_logger.LogError(ex, "{ExtractorType} failed to extract keyframes", nameof(MatroskaKeyframeExtractor));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!string.IsNullOrEmpty(ffToolPath))
|
try
|
||||||
{
|
{
|
||||||
return FfToolKeyframeExtractor.GetKeyframeData(ffToolPath, filePath);
|
return FfToolKeyframeExtractor.GetKeyframeData(ffToolPath, filePath);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "{ExtractorType} failed to extract keyframes", nameof(FfToolKeyframeExtractor));
|
||||||
|
}
|
||||||
|
|
||||||
return FfProbeKeyframeExtractor.GetKeyframeData(ffProbePath, filePath);
|
try
|
||||||
|
{
|
||||||
|
return FfProbeKeyframeExtractor.GetKeyframeData(ffProbePath, filePath);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
_logger.LogError(ex, "{ExtractorType} failed to extract keyframes", nameof(FfProbeKeyframeExtractor));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new KeyframeData(0, Array.Empty<long>());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -49,7 +49,7 @@ namespace Jellyfin.MediaEncoding.Keyframes.Matroska
|
||||||
|
|
||||||
if (trackNumber == videoTrackNumber)
|
if (trackNumber == videoTrackNumber)
|
||||||
{
|
{
|
||||||
keyframes.Add(ScaleToNanoseconds(cueTime, info.TimestampScale));
|
keyframes.Add(ScaleToTicks(cueTime, info.TimestampScale));
|
||||||
}
|
}
|
||||||
|
|
||||||
reader.LeaveContainer();
|
reader.LeaveContainer();
|
||||||
|
@ -57,17 +57,17 @@ namespace Jellyfin.MediaEncoding.Keyframes.Matroska
|
||||||
|
|
||||||
reader.LeaveContainer();
|
reader.LeaveContainer();
|
||||||
|
|
||||||
var result = new KeyframeData(ScaleToNanoseconds(info.Duration ?? 0, info.TimestampScale), keyframes);
|
var result = new KeyframeData(ScaleToTicks(info.Duration ?? 0, info.TimestampScale), keyframes);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long ScaleToNanoseconds(ulong unscaledValue, long timestampScale)
|
private static long ScaleToTicks(ulong unscaledValue, long timestampScale)
|
||||||
{
|
{
|
||||||
// TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns
|
// TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns
|
||||||
return (long)unscaledValue * timestampScale / 100;
|
return (long)unscaledValue * timestampScale / 100;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static long ScaleToNanoseconds(double unscaledValue, long timestampScale)
|
private static long ScaleToTicks(double unscaledValue, long timestampScale)
|
||||||
{
|
{
|
||||||
// TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns
|
// TimestampScale is in nanoseconds, scale it to get the value in ticks, 1 tick == 100 ns
|
||||||
return Convert.ToInt64(unscaledValue * timestampScale / 100);
|
return Convert.ToInt64(unscaledValue * timestampScale / 100);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user