diff --git a/Emby.Server.Implementations/Library/MediaSourceManager.cs b/Emby.Server.Implementations/Library/MediaSourceManager.cs
index fdb4c7328b..f1cc58f8bd 100644
--- a/Emby.Server.Implementations/Library/MediaSourceManager.cs
+++ b/Emby.Server.Implementations/Library/MediaSourceManager.cs
@@ -127,6 +127,11 @@ namespace Emby.Server.Implementations.Library
return true;
}
+ if (stream.IsVobSubSubtitleStream)
+ {
+ return true;
+ }
+
return false;
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 8ad66fce40..8ec80b2c7e 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -220,12 +220,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
Path = outputPath,
Protocol = MediaProtocol.File,
Format = outputFormat,
- IsExternal = false
+ IsExternal = MediaStream.IsVobSubFormat(outputFormat)
};
}
- var currentFormat = subtitleStream.Codec ?? Path.GetExtension(subtitleStream.Path)
- .TrimStart('.');
+ var currentFormat = subtitleStream.Codec ?? Path.GetExtension(subtitleStream.Path).TrimStart('.');
// Handle PGS subtitles as raw streams for the client to render
if (MediaStream.IsPgsFormat(currentFormat))
@@ -475,6 +474,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return subtitleStream.Codec;
}
+ else if (MediaStream.IsVobSubFormat(subtitleStream.Codec))
+ {
+ return "mks";
+ }
else
{
return "srt";
@@ -488,6 +491,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return "sup";
}
+ else if (MediaStream.IsVobSubFormat(subtitleStream.Codec))
+ {
+ // FFmpeg cannot mux VobSub subtitle streams back into the .idx/.sub pair, so we use .mks container instead.
+ return "mks";
+ }
else
{
return GetExtractableSubtitleFormat(subtitleStream);
@@ -500,7 +508,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|| string.Equals(codec, "ssa", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "srt", StringComparison.OrdinalIgnoreCase)
|| string.Equals(codec, "subrip", StringComparison.OrdinalIgnoreCase)
- || string.Equals(codec, "pgssub", StringComparison.OrdinalIgnoreCase);
+ || string.Equals(codec, "pgssub", StringComparison.OrdinalIgnoreCase)
+ || MediaStream.IsVobSubFormat(codec);
}
///
@@ -516,7 +525,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
foreach (var subtitleStream in subtitleStreams)
{
- if (subtitleStream.IsExternal && !subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
+ if (subtitleStream.IsExternal
+ && !subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase))
{
continue;
}
@@ -603,6 +613,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
var outputCodec = IsCodecCopyable(subtitleStream.Codec) ? "copy" : "srt";
+ // FFmpeg does not provide an .idx/.sub muxer, so VobSub streams must be written as MKS files.
+ var outputFormatOption = MediaStream.IsVobSubFormat(subtitleStream.Codec) ? " -f matroska" : string.Empty;
var streamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream);
if (streamIndex == -1)
@@ -616,9 +628,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
outputPaths.Add(outputPath);
args += string.Format(
CultureInfo.InvariantCulture,
- " -map 0:{0} -an -vn -c:s {1} -flush_packets 1 \"{2}\"",
+ " -map 0:{0} -an -vn -c:s {1}{2} -flush_packets 1 \"{3}\"",
streamIndex,
outputCodec,
+ outputFormatOption,
outputPath);
}
@@ -653,6 +666,8 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
var outputCodec = IsCodecCopyable(subtitleStream.Codec) ? "copy" : "srt";
+ // FFmpeg does not provide an .idx/.sub muxer, so VobSub streams must be written as MKS files.
+ var outputFormatOption = MediaStream.IsVobSubFormat(subtitleStream.Codec) ? " -f matroska" : string.Empty;
var streamIndex = EncodingHelper.FindIndex(mediaSource.MediaStreams, subtitleStream);
if (streamIndex == -1)
@@ -666,18 +681,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
outputPaths.Add(outputPath);
args += string.Format(
CultureInfo.InvariantCulture,
- " -map 0:{0} -an -vn -c:s {1} -flush_packets 1 \"{2}\"",
+ " -map 0:{0} -an -vn -c:s {1}{2} -flush_packets 1 \"{3}\"",
streamIndex,
outputCodec,
+ outputFormatOption,
outputPath);
}
- if (outputPaths.Count == 0)
+ if (outputPaths.Count > 0)
{
- return;
+ await ExtractSubtitlesForFile(inputPath, args, outputPaths, cancellationToken).ConfigureAwait(false);
}
-
- await ExtractSubtitlesForFile(inputPath, args, outputPaths, cancellationToken).ConfigureAwait(false);
}
private async Task ExtractSubtitlesForFile(
diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs
index 44697837ca..2eb221b495 100644
--- a/MediaBrowser.Model/Dlna/StreamBuilder.cs
+++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs
@@ -575,7 +575,12 @@ namespace MediaBrowser.Model.Dlna
{
foreach (var profile in subtitleProfiles)
{
- if (profile.Method == SubtitleDeliveryMethod.External && string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase))
+ if (profile.Method == SubtitleDeliveryMethod.External
+ && (string.Equals(profile.Format, stream.Codec, StringComparison.OrdinalIgnoreCase)
+ // FFmpeg cannot mux VobSub back into an .idx/.sub pair, so extracted VobSub streams are exposed as .mks.
+ || (string.Equals(profile.Format, "mks", StringComparison.OrdinalIgnoreCase)
+ && stream.IsVobSubSubtitleStream
+ && (!stream.IsExternal || stream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase)))))
{
return stream.Index;
}
@@ -1564,10 +1569,17 @@ namespace MediaBrowser.Model.Dlna
continue;
}
- if ((profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) ||
+ // FFmpeg cannot mux VobSub back into an .idx/.sub pair, so extracted VobSub streams are matched against external .mks delivery profiles.
+ bool isVobSubMksProfile = string.Equals(profile.Format, "mks", StringComparison.OrdinalIgnoreCase)
+ && subtitleStream.IsVobSubSubtitleStream
+ && (!subtitleStream.IsExternal || subtitleStream.Path.EndsWith(".mks", StringComparison.OrdinalIgnoreCase));
+
+ if ((profile.Method == SubtitleDeliveryMethod.External
+ && (isVobSubMksProfile || subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format))) ||
(profile.Method == SubtitleDeliveryMethod.Hls && subtitleStream.IsTextSubtitleStream))
{
- bool requiresConversion = !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase);
+ bool requiresConversion = !isVobSubMksProfile
+ && !string.Equals(subtitleStream.Codec, profile.Format, StringComparison.OrdinalIgnoreCase);
if (!requiresConversion)
{
diff --git a/MediaBrowser.Model/Entities/MediaStream.cs b/MediaBrowser.Model/Entities/MediaStream.cs
index dad4a6e149..f057714bea 100644
--- a/MediaBrowser.Model/Entities/MediaStream.cs
+++ b/MediaBrowser.Model/Entities/MediaStream.cs
@@ -644,13 +644,32 @@ namespace MediaBrowser.Model.Entities
}
}
+ [JsonIgnore]
+ public bool IsVobSubSubtitleStream
+ {
+ get
+ {
+ if (Type != MediaStreamType.Subtitle)
+ {
+ return false;
+ }
+
+ if (string.IsNullOrEmpty(Codec) && !IsExternal)
+ {
+ return false;
+ }
+
+ return IsVobSubFormat(Codec);
+ }
+ }
+
///
/// Gets a value indicating whether this is a subtitle steam that is extractable by ffmpeg.
/// All text-based and pgs subtitles can be extracted.
///
/// true if this is a extractable subtitle steam otherwise, false.
[JsonIgnore]
- public bool IsExtractableSubtitleStream => IsTextSubtitleStream || IsPgsSubtitleStream;
+ public bool IsExtractableSubtitleStream => IsTextSubtitleStream || IsPgsSubtitleStream || IsVobSubSubtitleStream;
///
/// Gets or sets a value indicating whether [supports external stream].
@@ -728,6 +747,7 @@ namespace MediaBrowser.Model.Entities
return codec.Contains("microdvd", StringComparison.OrdinalIgnoreCase)
|| (!codec.Contains("pgs", StringComparison.OrdinalIgnoreCase)
&& !codec.Contains("dvdsub", StringComparison.OrdinalIgnoreCase)
+ && !codec.Contains("vobsub", StringComparison.OrdinalIgnoreCase)
&& !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase)
&& !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase));
@@ -741,6 +761,14 @@ namespace MediaBrowser.Model.Entities
|| string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase);
}
+ public static bool IsVobSubFormat(string format)
+ {
+ string codec = format ?? string.Empty;
+
+ return codec.Contains("dvdsub", StringComparison.OrdinalIgnoreCase)
+ || codec.Contains("vobsub", StringComparison.OrdinalIgnoreCase);
+ }
+
public bool SupportsSubtitleConversionTo(string toCodec)
{
if (!IsTextSubtitleStream)