mirror of
https://github.com/jellyfin/jellyfin.git
synced 2025-05-31 04:05:50 -04:00
Allow streaming of raw PGS subtitles without transcoding (#12056)
This commit is contained in:
parent
3262f8dc2a
commit
fc1bee30a6
@ -113,6 +113,11 @@ namespace Emby.Server.Implementations.Library
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (stream.IsPgsSubtitleStream)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -198,10 +198,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
{
|
{
|
||||||
if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
|
if (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
await ExtractAllTextSubtitles(mediaSource, cancellationToken).ConfigureAwait(false);
|
await ExtractAllExtractableSubtitles(mediaSource, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
var outputFormat = GetTextSubtitleFormat(subtitleStream);
|
var outputFileExtension = GetExtractableSubtitleFileExtension(subtitleStream);
|
||||||
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFormat);
|
var outputFormat = GetExtractableSubtitleFormat(subtitleStream);
|
||||||
|
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + outputFileExtension);
|
||||||
|
|
||||||
return new SubtitleInfo()
|
return new SubtitleInfo()
|
||||||
{
|
{
|
||||||
@ -215,6 +216,18 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec)
|
var currentFormat = (Path.GetExtension(subtitleStream.Path) ?? subtitleStream.Codec)
|
||||||
.TrimStart('.');
|
.TrimStart('.');
|
||||||
|
|
||||||
|
// Handle PGS subtitles as raw streams for the client to render
|
||||||
|
if (MediaStream.IsPgsFormat(currentFormat))
|
||||||
|
{
|
||||||
|
return new SubtitleInfo()
|
||||||
|
{
|
||||||
|
Path = subtitleStream.Path,
|
||||||
|
Protocol = _mediaSourceManager.GetPathProtocol(subtitleStream.Path),
|
||||||
|
Format = "pgssub",
|
||||||
|
IsExternal = true
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
// Fallback to ffmpeg conversion
|
// Fallback to ffmpeg conversion
|
||||||
if (!_subtitleParser.SupportsFileExtension(currentFormat))
|
if (!_subtitleParser.SupportsFileExtension(currentFormat))
|
||||||
{
|
{
|
||||||
@ -428,10 +441,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
_logger.LogInformation("ffmpeg subtitle conversion succeeded for {Path}", inputPath);
|
_logger.LogInformation("ffmpeg subtitle conversion succeeded for {Path}", inputPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
private string GetTextSubtitleFormat(MediaStream subtitleStream)
|
private string GetExtractableSubtitleFormat(MediaStream subtitleStream)
|
||||||
{
|
{
|
||||||
if (string.Equals(subtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|
if (string.Equals(subtitleStream.Codec, "ass", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(subtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase))
|
|| string.Equals(subtitleStream.Codec, "ssa", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(subtitleStream.Codec, "pgssub", StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
return subtitleStream.Codec;
|
return subtitleStream.Codec;
|
||||||
}
|
}
|
||||||
@ -441,21 +455,35 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private string GetExtractableSubtitleFileExtension(MediaStream subtitleStream)
|
||||||
|
{
|
||||||
|
// Using .pgssub as file extension is not allowed by ffmpeg. The file extension for pgs subtitles is .sup.
|
||||||
|
if (string.Equals(subtitleStream.Codec, "pgssub", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
return "sup";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return GetExtractableSubtitleFormat(subtitleStream);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private bool IsCodecCopyable(string codec)
|
private bool IsCodecCopyable(string codec)
|
||||||
{
|
{
|
||||||
return string.Equals(codec, "ass", StringComparison.OrdinalIgnoreCase)
|
return string.Equals(codec, "ass", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(codec, "ssa", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(codec, "ssa", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(codec, "srt", StringComparison.OrdinalIgnoreCase)
|
|| string.Equals(codec, "srt", StringComparison.OrdinalIgnoreCase)
|
||||||
|| string.Equals(codec, "subrip", StringComparison.OrdinalIgnoreCase);
|
|| string.Equals(codec, "subrip", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(codec, "pgssub", StringComparison.OrdinalIgnoreCase);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Extracts all text subtitles.
|
/// Extracts all extractable subtitles (text and pgs).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mediaSource">The mediaSource.</param>
|
/// <param name="mediaSource">The mediaSource.</param>
|
||||||
/// <param name="cancellationToken">The cancellation token.</param>
|
/// <param name="cancellationToken">The cancellation token.</param>
|
||||||
/// <returns>Task.</returns>
|
/// <returns>Task.</returns>
|
||||||
private async Task ExtractAllTextSubtitles(MediaSourceInfo mediaSource, CancellationToken cancellationToken)
|
private async Task ExtractAllExtractableSubtitles(MediaSourceInfo mediaSource, CancellationToken cancellationToken)
|
||||||
{
|
{
|
||||||
var locks = new List<IDisposable>();
|
var locks = new List<IDisposable>();
|
||||||
var extractableStreams = new List<MediaStream>();
|
var extractableStreams = new List<MediaStream>();
|
||||||
@ -463,11 +491,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var subtitleStreams = mediaSource.MediaStreams
|
var subtitleStreams = mediaSource.MediaStreams
|
||||||
.Where(stream => stream is { IsTextSubtitleStream: true, SupportsExternalStream: true, IsExternal: false });
|
.Where(stream => stream is { IsExtractableSubtitleStream: true, SupportsExternalStream: true, IsExternal: false });
|
||||||
|
|
||||||
foreach (var subtitleStream in subtitleStreams)
|
foreach (var subtitleStream in subtitleStreams)
|
||||||
{
|
{
|
||||||
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetTextSubtitleFormat(subtitleStream));
|
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetExtractableSubtitleFileExtension(subtitleStream));
|
||||||
|
|
||||||
var releaser = await _semaphoreLocks.LockAsync(outputPath, cancellationToken).ConfigureAwait(false);
|
var releaser = await _semaphoreLocks.LockAsync(outputPath, cancellationToken).ConfigureAwait(false);
|
||||||
|
|
||||||
@ -483,7 +511,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
|
|
||||||
if (extractableStreams.Count > 0)
|
if (extractableStreams.Count > 0)
|
||||||
{
|
{
|
||||||
await ExtractAllTextSubtitlesInternal(mediaSource, extractableStreams, cancellationToken).ConfigureAwait(false);
|
await ExtractAllExtractableSubtitlesInternal(mediaSource, extractableStreams, cancellationToken).ConfigureAwait(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
@ -496,7 +524,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ExtractAllTextSubtitlesInternal(
|
private async Task ExtractAllExtractableSubtitlesInternal(
|
||||||
MediaSourceInfo mediaSource,
|
MediaSourceInfo mediaSource,
|
||||||
List<MediaStream> subtitleStreams,
|
List<MediaStream> subtitleStreams,
|
||||||
CancellationToken cancellationToken)
|
CancellationToken cancellationToken)
|
||||||
@ -510,7 +538,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
|||||||
|
|
||||||
foreach (var subtitleStream in subtitleStreams)
|
foreach (var subtitleStream in subtitleStreams)
|
||||||
{
|
{
|
||||||
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetTextSubtitleFormat(subtitleStream));
|
var outputPath = GetSubtitleCachePath(mediaSource, subtitleStream.Index, "." + GetExtractableSubtitleFileExtension(subtitleStream));
|
||||||
var outputCodec = IsCodecCopyable(subtitleStream.Codec) ? "copy" : "srt";
|
var outputCodec = IsCodecCopyable(subtitleStream.Codec) ? "copy" : "srt";
|
||||||
var streamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream);
|
var streamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream);
|
||||||
|
|
||||||
|
@ -585,6 +585,31 @@ namespace MediaBrowser.Model.Entities
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public bool IsPgsSubtitleStream
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
if (Type != MediaStreamType.Subtitle)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(Codec) && !IsExternal)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return IsPgsFormat(Codec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether this is a subtitle steam that is extractable by ffmpeg.
|
||||||
|
/// All text-based and pgs subtitles can be extracted.
|
||||||
|
/// </summary>
|
||||||
|
/// <value><c>true</c> if this is a extractable subtitle steam otherwise, <c>false</c>.</value>
|
||||||
|
public bool IsExtractableSubtitleStream => IsTextSubtitleStream || IsPgsSubtitleStream;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets a value indicating whether [supports external stream].
|
/// Gets or sets a value indicating whether [supports external stream].
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -666,6 +691,14 @@ namespace MediaBrowser.Model.Entities
|
|||||||
&& !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase));
|
&& !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsPgsFormat(string format)
|
||||||
|
{
|
||||||
|
string codec = format ?? string.Empty;
|
||||||
|
|
||||||
|
return codec.Contains("pgs", StringComparison.OrdinalIgnoreCase)
|
||||||
|
|| string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase);
|
||||||
|
}
|
||||||
|
|
||||||
public bool SupportsSubtitleConversionTo(string toCodec)
|
public bool SupportsSubtitleConversionTo(string toCodec)
|
||||||
{
|
{
|
||||||
if (!IsTextSubtitleStream)
|
if (!IsTextSubtitleStream)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user