aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding')
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs3
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs4
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs82
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs71
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj7
-rw-r--r--MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs8
-rw-r--r--MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs14
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs96
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs10
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs8
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs85
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs16
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs15
13 files changed, 323 insertions, 96 deletions
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index 3f177a9fa..f02999370 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -21,7 +21,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
{
public class AttachmentExtractor : IAttachmentExtractor, IDisposable
{
- private readonly ILogger _logger;
+ private readonly ILogger<AttachmentExtractor> _logger;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly IMediaEncoder _mediaEncoder;
@@ -269,7 +269,6 @@ namespace MediaBrowser.MediaEncoding.Attachments
if (disposing)
{
-
}
_disposed = true;
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
index 3260f3051..ccfae2fa5 100644
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
+++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs
@@ -9,7 +9,7 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.BdInfo
{
/// <summary>
- /// Class BdInfoExaminer
+ /// Class BdInfoExaminer.
/// </summary>
public class BdInfoExaminer : IBlurayExaminer
{
@@ -41,7 +41,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo
var outputStream = new BlurayDiscInfo
{
- MediaStreams = new MediaStream[] { }
+ MediaStreams = Array.Empty<MediaStream>()
};
if (playlist == null)
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 6e036d24c..0fd0239b4 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -14,23 +14,45 @@ namespace MediaBrowser.MediaEncoding.Encoder
private static readonly string[] requiredDecoders = new[]
{
+ "h264",
+ "hevc",
"mpeg2video",
+ "mpeg4",
+ "msmpeg4",
+ "dts",
+ "ac3",
+ "aac",
+ "mp3",
"h264_qsv",
"hevc_qsv",
"mpeg2_qsv",
- "mpeg2_mmal",
- "mpeg4_mmal",
"vc1_qsv",
- "vc1_mmal",
+ "vp8_qsv",
+ "vp9_qsv",
"h264_cuvid",
"hevc_cuvid",
- "dts",
- "ac3",
- "aac",
- "mp3",
- "h264",
+ "mpeg2_cuvid",
+ "vc1_cuvid",
+ "mpeg4_cuvid",
+ "vp8_cuvid",
+ "vp9_cuvid",
"h264_mmal",
- "hevc"
+ "mpeg2_mmal",
+ "mpeg4_mmal",
+ "vc1_mmal",
+ "h264_mediacodec",
+ "hevc_mediacodec",
+ "mpeg2_mediacodec",
+ "mpeg4_mediacodec",
+ "vp8_mediacodec",
+ "vp9_mediacodec",
+ "h264_opencl",
+ "hevc_opencl",
+ "mpeg2_opencl",
+ "mpeg4_opencl",
+ "vp8_opencl",
+ "vp9_opencl",
+ "vc1_opencl"
};
private static readonly string[] requiredEncoders = new[]
@@ -43,22 +65,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
"libvpx-vp9",
"aac",
"libfdk_aac",
+ "ac3",
"libmp3lame",
"libopus",
"libvorbis",
"srt",
- "h264_nvenc",
- "hevc_nvenc",
+ "h264_amf",
+ "hevc_amf",
"h264_qsv",
"hevc_qsv",
- "h264_omx",
- "hevc_omx",
+ "h264_nvenc",
+ "hevc_nvenc",
"h264_vaapi",
"hevc_vaapi",
+ "h264_omx",
+ "hevc_omx",
"h264_v4l2m2m",
- "ac3",
- "h264_amf",
- "hevc_amf"
+ "h264_videotoolbox",
+ "hevc_videotoolbox"
};
// Try and use the individual library versions to determine a FFmpeg version
@@ -66,6 +90,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// $ ffmpeg -version | perl -ne ' print "$1=$2.$3," if /^(lib\w+)\s+(\d+)\.\s*(\d+)/'
private static readonly IReadOnlyDictionary<string, Version> _ffmpegVersionMap = new Dictionary<string, Version>
{
+ { "libavutil=56.51,libavcodec=58.91,libavformat=58.45,libavdevice=58.10,libavfilter=7.85,libswscale=5.7,libswresample=3.7,libpostproc=55.7,", new Version(4, 3) },
{ "libavutil=56.31,libavcodec=58.54,libavformat=58.29,libavdevice=58.8,libavfilter=7.57,libswscale=5.5,libswresample=3.5,libpostproc=55.5,", new Version(4, 2) },
{ "libavutil=56.22,libavcodec=58.35,libavformat=58.20,libavdevice=58.5,libavfilter=7.40,libswscale=5.3,libswresample=3.3,libpostproc=55.3,", new Version(4, 1) },
{ "libavutil=56.14,libavcodec=58.18,libavformat=58.12,libavdevice=58.3,libavfilter=7.16,libswscale=5.1,libswresample=3.1,libpostproc=55.1,", new Version(4, 0) },
@@ -159,6 +184,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
public IEnumerable<string> GetEncoders() => GetCodecs(Codec.Encoder);
+ public IEnumerable<string> GetHwaccels() => GetHwaccelTypes();
+
/// <summary>
/// Using the output from "ffmpeg -version" work out the FFmpeg version.
/// For pre-built binaries the first line should contain a string like "ffmpeg version x.y", which is easy
@@ -218,6 +245,29 @@ namespace MediaBrowser.MediaEncoding.Encoder
Decoder
}
+ private IEnumerable<string> GetHwaccelTypes()
+ {
+ string output = null;
+ try
+ {
+ output = GetProcessOutput(_encoderPath, "-hwaccels");
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Error detecting available hwaccel types");
+ }
+
+ if (string.IsNullOrWhiteSpace(output))
+ {
+ return Enumerable.Empty<string>();
+ }
+
+ var found = output.Split(new char[] {'\r','\n'}, StringSplitOptions.RemoveEmptyEntries).Skip(1).Distinct().ToList();
+ _logger.LogInformation("Available hwaccel types: {Types}", found);
+
+ return found;
+ }
+
private IEnumerable<string> GetCodecs(Codec codec)
{
string codecstr = codec == Codec.Encoder ? "encoders" : "decoders";
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index 359330449..62fdbc618 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -19,14 +19,14 @@ using MediaBrowser.Model.Globalization;
using MediaBrowser.Model.IO;
using MediaBrowser.Model.MediaInfo;
using MediaBrowser.Model.System;
-using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System.Diagnostics;
+using Microsoft.Extensions.Configuration;
namespace MediaBrowser.MediaEncoding.Encoder
{
/// <summary>
- /// Class MediaEncoder
+ /// Class MediaEncoder.
/// </summary>
public class MediaEncoder : IMediaEncoder, IDisposable
{
@@ -35,12 +35,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// </summary>
internal const int DefaultImageExtractionTimeout = 5000;
- private readonly ILogger _logger;
+ private readonly ILogger<MediaEncoder> _logger;
private readonly IServerConfigurationManager _configurationManager;
private readonly IFileSystem _fileSystem;
private readonly ILocalizationManager _localization;
- private readonly Func<ISubtitleEncoder> _subtitleEncoder;
- private readonly IConfiguration _configuration;
+ private readonly Lazy<EncodingHelper> _encodingHelperFactory;
private readonly string _startupOptionFFmpegPath;
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
@@ -48,9 +47,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly object _runningProcessesLock = new object();
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
- private EncodingHelper _encodingHelper;
-
- private string _ffmpegPath;
+ private string _ffmpegPath = string.Empty;
private string _ffprobePath;
public MediaEncoder(
@@ -58,23 +55,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
ILocalizationManager localization,
- Func<ISubtitleEncoder> subtitleEncoder,
- IConfiguration configuration,
- string startupOptionsFFmpegPath)
+ Lazy<EncodingHelper> encodingHelperFactory,
+ IConfiguration config)
{
_logger = logger;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
- _startupOptionFFmpegPath = startupOptionsFFmpegPath;
- _subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
+ _encodingHelperFactory = encodingHelperFactory;
+ _startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
}
- private EncodingHelper EncodingHelper
- => LazyInitializer.EnsureInitialized(
- ref _encodingHelper,
- () => new EncodingHelper(this, _fileSystem, _subtitleEncoder(), _configuration));
+ private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
/// <inheritdoc />
public string EncoderPath => _ffmpegPath;
@@ -120,9 +112,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
SetAvailableDecoders(validator.GetDecoders());
SetAvailableEncoders(validator.GetEncoders());
+ SetAvailableHwaccels(validator.GetHwaccels());
}
- _logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation, _ffmpegPath ?? string.Empty);
+ _logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty);
}
/// <summary>
@@ -135,7 +128,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
string newPath;
- _logger.LogInformation("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty);
+ _logger.LogInformation("Attempting to update encoder path to {Path}. pathType: {PathType}", path ?? string.Empty, pathType ?? string.Empty);
if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
{
@@ -174,7 +167,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// Validates the supplied FQPN to ensure it is a ffmpeg utility.
/// If checks pass, global variable FFmpegPath and EncoderLocation are updated.
/// </summary>
- /// <param name="path">FQPN to test</param>
+ /// <param name="path">FQPN to test.</param>
/// <param name="location">Location (External, Custom, System) of tool</param>
/// <returns></returns>
private bool ValidatePath(string path, FFmpegLocation location)
@@ -189,7 +182,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (!rc)
{
- _logger.LogWarning("FFmpeg: {0}: Failed version check: {1}", location, path);
+ _logger.LogWarning("FFmpeg: {Location}: Failed version check: {Path}", location, path);
}
// ToDo - Enable the ffmpeg validator. At the moment any version can be used.
@@ -200,18 +193,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
else
{
- _logger.LogWarning("FFmpeg: {0}: File not found: {1}", location, path);
+ _logger.LogWarning("FFmpeg: {Location}: File not found: {Path}", location, path);
}
}
return rc;
}
- private string GetEncoderPathFromDirectory(string path, string filename)
+ private string GetEncoderPathFromDirectory(string path, string filename, bool recursive = false)
{
try
{
- var files = _fileSystem.GetFilePaths(path);
+ var files = _fileSystem.GetFilePaths(path, recursive);
var excludeExtensions = new[] { ".c" };
@@ -232,11 +225,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <returns></returns>
private string ExistsOnSystemPath(string fileName)
{
- string inJellyfinPath = GetEncoderPathFromDirectory(System.AppContext.BaseDirectory, fileName);
+ var inJellyfinPath = GetEncoderPathFromDirectory(AppContext.BaseDirectory, fileName, recursive: true);
if (!string.IsNullOrEmpty(inJellyfinPath))
{
return inJellyfinPath;
}
+
var values = Environment.GetEnvironmentVariable("PATH");
foreach (var path in values.Split(Path.PathSeparator))
@@ -256,14 +250,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
public void SetAvailableEncoders(IEnumerable<string> list)
{
_encoders = list.ToList();
- //_logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
+ // _logger.Info("Supported encoders: {0}", string.Join(",", list.ToArray()));
}
private List<string> _decoders = new List<string>();
public void SetAvailableDecoders(IEnumerable<string> list)
{
_decoders = list.ToList();
- //_logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
+ // _logger.Info("Supported decoders: {0}", string.Join(",", list.ToArray()));
+ }
+
+ private List<string> _hwaccels = new List<string>();
+ public void SetAvailableHwaccels(IEnumerable<string> list)
+ {
+ _hwaccels = list.ToList();
+ //_logger.Info("Supported hwaccels: {0}", string.Join(",", list.ToArray()));
}
public bool SupportsEncoder(string encoder)
@@ -276,6 +277,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
return _decoders.Contains(decoder, StringComparer.OrdinalIgnoreCase);
}
+ public bool SupportsHwaccel(string hwaccel)
+ {
+ return _hwaccels.Contains(hwaccel, StringComparer.OrdinalIgnoreCase);
+ }
+
public bool CanEncodeToAudioCodec(string codec)
{
if (string.Equals(codec, "opus", StringComparison.OrdinalIgnoreCase))
@@ -434,7 +440,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
/// <summary>
- /// The us culture
+ /// The us culture.
/// </summary>
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -509,11 +515,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
break;
case Video3DFormat.FullSideBySide:
vf = "crop=iw/2:ih:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- //fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to 600.
+ // fsbs crop width in half,set the display aspect,crop out any black bars we may have made the scale width to 600.
break;
case Video3DFormat.HalfTopAndBottom:
vf = "crop=iw:ih/2:0:0,scale=(iw*2):ih),setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
- //htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600
+ // htab crop heigh in half,scale to correct size, set the display aspect,crop out any black bars we may have made the scale width to 600
break;
case Video3DFormat.FullTopAndBottom:
vf = "crop=iw:ih/2:0:0,setdar=dar=a,crop=min(iw\\,ih*dar):min(ih\\,iw/dar):(iw-min(iw\\,iw*sar))/2:(ih - min (ih\\,ih/sar))/2,setsar=sar=1,scale=600:trunc(600/dar/2)*2";
@@ -901,7 +907,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return minSizeVobs.Count == 0 ? vobs.Select(i => i.FullName) : minSizeVobs.Select(i => i.FullName);
}
- _logger.LogWarning("Could not determine vob file list for {0} using DvdLib. Will scan using file sizes.", path);
+ _logger.LogWarning("Could not determine vob file list for {Path} using DvdLib. Will scan using file sizes.", path);
}
var files = allVobs
@@ -929,7 +935,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
var fileParts = _fileSystem.GetFileNameWithoutExtension(f).Split('_');
return fileParts.Length == 3 && string.Equals(title, fileParts[1], StringComparison.OrdinalIgnoreCase);
-
}).ToList();
// If this resulted in not getting any vobs, just take them all
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index a312dcd70..aeb4dbe73 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -1,5 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
+ <!-- ProjectGuid is only included as a requirement for SonarQube analysis -->
+ <PropertyGroup>
+ <ProjectGuid>{960295EE-4AF4-4440-A525-B4C295B01A61}</ProjectGuid>
+ </PropertyGroup>
+
<PropertyGroup>
<TargetFramework>netstandard2.1</TargetFramework>
<GenerateAssemblyInfo>false</GenerateAssemblyInfo>
@@ -18,7 +23,7 @@
<ItemGroup>
<PackageReference Include="BDInfo" Version="0.7.6.1" />
- <PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.0" />
+ <PackageReference Include="System.Text.Encoding.CodePages" Version="4.7.1" />
<PackageReference Include="UTF.Unknown" Version="2.3.0" />
</ItemGroup>
diff --git a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
index 78dc7b607..3aa296f7f 100644
--- a/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
+++ b/MediaBrowser.MediaEncoding/Probing/FFProbeHelpers.cs
@@ -35,7 +35,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets a string from an FFProbeResult tags dictionary
+ /// Gets a string from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -52,7 +52,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets an int from an FFProbeResult tags dictionary
+ /// Gets an int from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -73,7 +73,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets a DateTime from an FFProbeResult tags dictionary
+ /// Gets a DateTime from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -94,7 +94,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Converts a dictionary to case insensitive
+ /// Converts a dictionary to case insensitive.
/// </summary>
/// <param name="dict">The dict.</param>
/// <returns>Dictionary{System.StringSystem.String}.</returns>
diff --git a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
index 0b2f1d231..a2ea0766a 100644
--- a/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
+++ b/MediaBrowser.MediaEncoding/Probing/MediaStreamInfo.cs
@@ -278,5 +278,19 @@ namespace MediaBrowser.MediaEncoding.Probing
/// <value>The disposition.</value>
[JsonPropertyName("disposition")]
public IReadOnlyDictionary<string, int> Disposition { get; set; }
+
+ /// <summary>
+ /// Gets or sets the color transfer.
+ /// </summary>
+ /// <value>The color transfer.</value>
+ [JsonPropertyName("color_transfer")]
+ public string ColorTransfer { get; set; }
+
+ /// <summary>
+ /// Gets or sets the color primaries.
+ /// </summary>
+ /// <value>The color primaries.</value>
+ [JsonPropertyName("color_primaries")]
+ public string ColorPrimaries { get; set; }
}
}
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index b24d97f4e..c85d963ed 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -93,6 +93,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
overview = FFProbeHelpers.GetDictionaryValue(tags, "description");
}
+
if (string.IsNullOrWhiteSpace(overview))
{
overview = FFProbeHelpers.GetDictionaryValue(tags, "desc");
@@ -274,10 +275,12 @@ namespace MediaBrowser.MediaEncoding.Probing
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
ReadFromDictNode(subtree, info);
}
+
break;
default:
reader.Skip();
@@ -319,6 +322,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
ProcessPairs(currentKey, pairs, info);
}
+
currentKey = reader.ReadElementContentAsString();
pairs = new List<NameValuePair>();
break;
@@ -332,6 +336,7 @@ namespace MediaBrowser.MediaEncoding.Probing
Value = value
});
}
+
break;
case "array":
if (reader.IsEmptyElement)
@@ -339,6 +344,7 @@ namespace MediaBrowser.MediaEncoding.Probing
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
if (!string.IsNullOrWhiteSpace(currentKey))
@@ -346,6 +352,7 @@ namespace MediaBrowser.MediaEncoding.Probing
pairs.AddRange(ReadValueArray(subtree));
}
}
+
break;
default:
reader.Skip();
@@ -381,6 +388,7 @@ namespace MediaBrowser.MediaEncoding.Probing
reader.Read();
continue;
}
+
using (var subtree = reader.ReadSubtree())
{
var dict = GetNameValuePair(subtree);
@@ -389,6 +397,7 @@ namespace MediaBrowser.MediaEncoding.Probing
pairs.Add(dict);
}
}
+
break;
default:
reader.Skip();
@@ -413,7 +422,6 @@ namespace MediaBrowser.MediaEncoding.Probing
.Where(i => !string.IsNullOrWhiteSpace(i))
.Distinct(StringComparer.OrdinalIgnoreCase)
.ToArray();
-
}
else if (string.Equals(key, "screenwriters", StringComparison.OrdinalIgnoreCase))
{
@@ -425,7 +433,6 @@ namespace MediaBrowser.MediaEncoding.Probing
Type = PersonType.Writer
});
}
-
}
else if (string.Equals(key, "producers", StringComparison.OrdinalIgnoreCase))
{
@@ -517,7 +524,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Converts ffprobe stream info to our MediaAttachment class
+ /// Converts ffprobe stream info to our MediaAttachment class.
/// </summary>
/// <param name="streamInfo">The stream info.</param>
/// <returns>MediaAttachments.</returns>
@@ -550,7 +557,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Converts ffprobe stream info to our MediaStream class
+ /// Converts ffprobe stream info to our MediaStream class.
/// </summary>
/// <param name="isAudio">if set to <c>true</c> [is info].</param>
/// <param name="streamInfo">The stream info.</param>
@@ -562,7 +569,7 @@ namespace MediaBrowser.MediaEncoding.Probing
if (string.Equals(streamInfo.CodecName, "mov_text", StringComparison.OrdinalIgnoreCase))
{
// Edit: but these are also sometimes subtitles?
- //return null;
+ // return null;
}
var stream = new MediaStream
@@ -684,7 +691,7 @@ namespace MediaBrowser.MediaEncoding.Probing
stream.BitDepth = streamInfo.BitsPerRawSample;
}
- //stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
+ // stream.IsAnamorphic = string.Equals(streamInfo.sample_aspect_ratio, "0:1", StringComparison.OrdinalIgnoreCase) ||
// string.Equals(stream.AspectRatio, "2.35:1", StringComparison.OrdinalIgnoreCase) ||
// string.Equals(stream.AspectRatio, "2.40:1", StringComparison.OrdinalIgnoreCase);
@@ -695,6 +702,16 @@ namespace MediaBrowser.MediaEncoding.Probing
{
stream.RefFrames = streamInfo.Refs;
}
+
+ if (!string.IsNullOrEmpty(streamInfo.ColorTransfer))
+ {
+ stream.ColorTransfer = streamInfo.ColorTransfer;
+ }
+
+ if (!string.IsNullOrEmpty(streamInfo.ColorPrimaries))
+ {
+ stream.ColorPrimaries = streamInfo.ColorPrimaries;
+ }
}
else
{
@@ -759,7 +776,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets a string from an FFProbeResult tags dictionary
+ /// Gets a string from an FFProbeResult tags dictionary.
/// </summary>
/// <param name="tags">The tags.</param>
/// <param name="key">The key.</param>
@@ -940,11 +957,12 @@ namespace MediaBrowser.MediaEncoding.Probing
{
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer });
}
+
audio.People = peoples.ToArray();
}
- //var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
- //if (!string.IsNullOrWhiteSpace(conductor))
+ // var conductor = FFProbeHelpers.GetDictionaryValue(tags, "conductor");
+ // if (!string.IsNullOrWhiteSpace(conductor))
//{
// foreach (var person in Split(conductor, false))
// {
@@ -952,8 +970,8 @@ namespace MediaBrowser.MediaEncoding.Probing
// }
//}
- //var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
- //if (!string.IsNullOrWhiteSpace(lyricist))
+ // var lyricist = FFProbeHelpers.GetDictionaryValue(tags, "lyricist");
+ // if (!string.IsNullOrWhiteSpace(lyricist))
//{
// foreach (var person in Split(lyricist, false))
// {
@@ -971,6 +989,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer });
}
+
audio.People = peoples.ToArray();
}
@@ -989,7 +1008,7 @@ namespace MediaBrowser.MediaEncoding.Probing
var artist = FFProbeHelpers.GetDictionaryValue(tags, "artist");
if (string.IsNullOrWhiteSpace(artist))
{
- audio.Artists = new string[] { };
+ audio.Artists = Array.Empty<string>();
}
else
{
@@ -1004,6 +1023,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist");
}
+
if (string.IsNullOrWhiteSpace(albumArtist))
{
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist");
@@ -1011,14 +1031,13 @@ namespace MediaBrowser.MediaEncoding.Probing
if (string.IsNullOrWhiteSpace(albumArtist))
{
- audio.AlbumArtists = new string[] { };
+ audio.AlbumArtists = Array.Empty<string>();
}
else
{
audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true)
.DistinctNames()
.ToArray();
-
}
if (audio.AlbumArtists.Length == 0)
@@ -1046,24 +1065,44 @@ namespace MediaBrowser.MediaEncoding.Probing
// These support mulitple values, but for now we only store the first.
var mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Artist Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbumArtist, mb);
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMARTISTID"));
+ }
+
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbumArtist, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Artist Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzArtist, mb);
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ARTISTID"));
+ }
+
+ audio.SetProviderId(MetadataProvider.MusicBrainzArtist, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Album Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzAlbum, mb);
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_ALBUMID"));
+ }
+
+ audio.SetProviderId(MetadataProvider.MusicBrainzAlbum, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Group Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzReleaseGroup, mb);
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASEGROUPID"));
+ }
+
+ audio.SetProviderId(MetadataProvider.MusicBrainzReleaseGroup, mb);
mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MusicBrainz Release Track Id"));
- if (mb == null) mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
- audio.SetProviderId(MetadataProviders.MusicBrainzTrack, mb);
+ if (mb == null)
+ {
+ mb = GetMultipleMusicBrainzId(FFProbeHelpers.GetDictionaryValue(tags, "MUSICBRAINZ_RELEASETRACKID"));
+ }
+
+ audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb);
}
private string GetMultipleMusicBrainzId(string value)
@@ -1147,7 +1186,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets the studios from the tags collection
+ /// Gets the studios from the tags collection.
/// </summary>
/// <param name="info">The info.</param>
/// <param name="tags">The tags.</param>
@@ -1168,6 +1207,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
continue;
}
+
if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase))
{
continue;
@@ -1184,7 +1224,7 @@ namespace MediaBrowser.MediaEncoding.Probing
}
/// <summary>
- /// Gets the genres from the tags collection
+ /// Gets the genres from the tags collection.
/// </summary>
/// <param name="info">The information.</param>
/// <param name="tags">The tags.</param>
@@ -1344,14 +1384,18 @@ namespace MediaBrowser.MediaEncoding.Probing
description = string.Join(" ", numbers, 1, numbers.Length - 1).Trim(); // Skip the first, concatenate the rest, clean up spaces and save it
}
else
+ {
throw new Exception(); // Switch to default parsing
+ }
}
catch // Default parsing
{
if (subtitle.Contains(".")) // skip the comment, keep the subtitle
description = string.Join(".", subtitle.Split('.'), 1, subtitle.Split('.').Length - 1).Trim(); // skip the first
else
+ {
description = subtitle.Trim(); // Clean up whitespaces and save it
+ }
}
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
index 293cf5ea5..0e2d70017 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssParser.cs
@@ -23,6 +23,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string line;
while (reader.ReadLine() != "[Events]")
{ }
+
var headers = ParseFieldHeaders(reader.ReadLine());
while ((line = reader.ReadLine()) != null)
@@ -33,10 +34,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
continue;
}
+
if (line.StartsWith("["))
+ {
break;
- if (string.IsNullOrEmpty(line))
- continue;
+ }
+
var subEvent = new SubtitleTrackEvent { Id = eventIndex.ToString(_usCulture) };
eventIndex++;
var sections = line.Substring(10).Split(',');
@@ -54,6 +57,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
trackEvents.Add(subEvent);
}
}
+
trackInfo.TrackEvents = trackEvents.ToArray();
return trackInfo;
}
@@ -110,11 +114,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
pre = s.Substring(0, 5) + "}";
}
+
int indexOfEnd = p.Text.IndexOf('}');
p.Text = p.Text.Remove(indexOfBegin, (indexOfEnd - indexOfBegin) + 1);
indexOfBegin = p.Text.IndexOf('{');
}
+
p.Text = pre + p.Text;
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
index c98dd1502..728efa788 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs
@@ -35,6 +35,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
continue;
}
+
var subEvent = new SubtitleTrackEvent { Id = line };
line = reader.ReadLine();
@@ -52,11 +53,15 @@ namespace MediaBrowser.MediaEncoding.Subtitles
_logger.LogWarning("Unrecognized line in srt: {0}", line);
continue;
}
+
subEvent.StartPositionTicks = GetTicks(time[0]);
var endTime = time[1];
var idx = endTime.IndexOf(" ", StringComparison.Ordinal);
if (idx > 0)
+ {
endTime = endTime.Substring(0, idx);
+ }
+
subEvent.EndPositionTicks = GetTicks(endTime);
var multiline = new List<string>();
while ((line = reader.ReadLine()) != null)
@@ -65,8 +70,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
break;
}
+
multiline.Add(line);
}
+
subEvent.Text = string.Join(ParserValues.NewLine, multiline);
subEvent.Text = subEvent.Text.Replace(@"\N", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
subEvent.Text = Regex.Replace(subEvent.Text, @"\{(?:\\\d?[\w.-]+(?:\([^\)]*\)|&H?[0-9A-Fa-f]+&|))+\}", string.Empty, RegexOptions.IgnoreCase);
@@ -76,6 +83,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
trackEvents.Add(subEvent);
}
}
+
trackInfo.TrackEvents = trackEvents.ToArray();
return trackInfo;
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
index b94d45165..77033c6b4 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -41,7 +41,9 @@ namespace MediaBrowser.MediaEncoding.Subtitles
lineNumber++;
if (!eventsStarted)
+ {
header.AppendLine(line);
+ }
if (line.Trim().ToLowerInvariant() == "[events]")
{
@@ -62,17 +64,29 @@ namespace MediaBrowser.MediaEncoding.Subtitles
for (int i = 0; i < format.Length; i++)
{
if (format[i].Trim().ToLowerInvariant() == "layer")
+ {
indexLayer = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "start")
+ {
indexStart = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "end")
+ {
indexEnd = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "text")
+ {
indexText = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "effect")
+ {
indexEffect = i;
+ }
else if (format[i].Trim().ToLowerInvariant() == "style")
+ {
indexStyle = i;
+ }
}
}
}
@@ -89,28 +103,48 @@ namespace MediaBrowser.MediaEncoding.Subtitles
string[] splittedLine;
if (s.StartsWith("dialogue:"))
+ {
splittedLine = line.Substring(10).Split(',');
+ }
else
+ {
splittedLine = line.Split(',');
+ }
for (int i = 0; i < splittedLine.Length; i++)
{
if (i == indexStart)
+ {
start = splittedLine[i].Trim();
+ }
else if (i == indexEnd)
+ {
end = splittedLine[i].Trim();
+ }
else if (i == indexLayer)
+ {
layer = splittedLine[i];
+ }
else if (i == indexEffect)
+ {
effect = splittedLine[i];
+ }
else if (i == indexText)
+ {
text = splittedLine[i];
+ }
else if (i == indexStyle)
+ {
style = splittedLine[i];
+ }
else if (i == indexName)
+ {
name = splittedLine[i];
+ }
else if (i > indexText)
+ {
text += "," + splittedLine[i];
+ }
}
try
@@ -130,11 +164,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
}
- //if (header.Length > 0)
- //subtitle.Header = header.ToString();
+ // if (header.Length > 0)
+ // subtitle.Header = header.ToString();
- //subtitle.Renumber(1);
+ // subtitle.Renumber(1);
}
+
trackInfo.TrackEvents = trackEvents.ToArray();
return trackInfo;
}
@@ -168,15 +203,23 @@ namespace MediaBrowser.MediaEncoding.Subtitles
CheckAndAddSubTags(ref fontName, ref extraTags, out italic);
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font face=\"" + fontName + "\"" + extraTags + ">");
+ }
int indexOfEndTag = text.IndexOf("{\\fn}", start);
if (indexOfEndTag > 0)
+ {
text = text.Remove(indexOfEndTag, "{\\fn}".Length).Insert(indexOfEndTag, "</font>");
+ }
else
+ {
text += "</font>";
+ }
}
}
@@ -193,15 +236,23 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font size=\"" + fontSize + "\"" + extraTags + ">");
+ }
int indexOfEndTag = text.IndexOf("{\\fs}", start);
if (indexOfEndTag > 0)
+ {
text = text.Remove(indexOfEndTag, "{\\fs}".Length).Insert(indexOfEndTag, "</font>");
+ }
else
+ {
text += "</font>";
+ }
}
}
}
@@ -225,14 +276,22 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
+ }
int indexOfEndTag = text.IndexOf("{\\c}", start);
if (indexOfEndTag > 0)
+ {
text = text.Remove(indexOfEndTag, "{\\c}".Length).Insert(indexOfEndTag, "</font>");
+ }
else
+ {
text += "</font>";
+ }
}
}
@@ -255,32 +314,41 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = text.Remove(start, end - start + 1);
if (italic)
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + "><i>");
+ }
else
+ {
text = text.Insert(start, "<font color=\"" + color + "\"" + extraTags + ">");
+ }
text += "</font>";
}
}
-
}
text = text.Replace(@"{\i1}", "<i>");
text = text.Replace(@"{\i0}", "</i>");
text = text.Replace(@"{\i}", "</i>");
if (CountTagInText(text, "<i>") > CountTagInText(text, "</i>"))
+ {
text += "</i>";
+ }
text = text.Replace(@"{\u1}", "<u>");
text = text.Replace(@"{\u0}", "</u>");
text = text.Replace(@"{\u}", "</u>");
if (CountTagInText(text, "<u>") > CountTagInText(text, "</u>"))
+ {
text += "</u>";
+ }
text = text.Replace(@"{\b1}", "<b>");
text = text.Replace(@"{\b0}", "</b>");
text = text.Replace(@"{\b}", "</b>");
if (CountTagInText(text, "<b>") > CountTagInText(text, "</b>"))
+ {
text += "</b>";
+ }
return text;
}
@@ -288,7 +356,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
private static bool IsInteger(string s)
{
if (int.TryParse(s, out var i))
+ {
return true;
+ }
+
return false;
}
@@ -300,9 +371,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
count++;
if (index == text.Length)
+ {
return count;
+ }
+
index = text.IndexOf(tag, index + 1);
}
+
return count;
}
@@ -330,6 +405,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
rest = string.Empty;
}
+
extraTags += " size=\"" + fontSize.Substring(2) + "\"";
}
else if (rest.StartsWith("fn") && rest.Length > 2)
@@ -345,6 +421,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
rest = string.Empty;
}
+
extraTags += " face=\"" + fontName.Substring(2) + "\"";
}
else if (rest.StartsWith("c") && rest.Length > 2)
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index ba171295e..f1aa8ea5f 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -25,7 +25,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
public class SubtitleEncoder : ISubtitleEncoder
{
private readonly ILibraryManager _libraryManager;
- private readonly ILogger _logger;
+ private readonly ILogger<SubtitleEncoder> _logger;
private readonly IApplicationPaths _appPaths;
private readonly IFileSystem _fileSystem;
private readonly IMediaEncoder _mediaEncoder;
@@ -115,6 +115,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
throw new ArgumentNullException(nameof(item));
}
+
if (string.IsNullOrWhiteSpace(mediaSourceId))
{
throw new ArgumentNullException(nameof(mediaSourceId));
@@ -271,8 +272,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
public string Path { get; set; }
+
public MediaProtocol Protocol { get; set; }
+
public string Format { get; set; }
+
public bool IsExternal { get; set; }
}
@@ -287,10 +291,12 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return new SrtParser(_logger);
}
+
if (string.Equals(format, SubtitleFormat.SSA, StringComparison.OrdinalIgnoreCase))
{
return new SsaParser();
}
+
if (string.Equals(format, SubtitleFormat.ASS, StringComparison.OrdinalIgnoreCase))
{
return new AssParser();
@@ -315,14 +321,17 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
return new JsonWriter();
}
+
if (string.Equals(format, SubtitleFormat.SRT, StringComparison.OrdinalIgnoreCase))
{
return new SrtWriter();
}
+
if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase))
{
return new VttWriter();
}
+
if (string.Equals(format, SubtitleFormat.TTML, StringComparison.OrdinalIgnoreCase))
{
return new TtmlWriter();
@@ -344,7 +353,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
/// <summary>
- /// The _semaphoreLocks
+ /// The _semaphoreLocks.
/// </summary>
private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
new ConcurrentDictionary<string, SemaphoreSlim>();
@@ -640,7 +649,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
}
catch (FileNotFoundException)
{
-
}
catch (IOException ex)
{
@@ -737,7 +745,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var charset = CharsetDetector.DetectFromStream(stream).Detected?.EncodingName;
// UTF16 is automatically converted to UTF8 by FFmpeg, do not specify a character encoding
- if ((path.EndsWith(".ass") || path.EndsWith(".ssa"))
+ if ((path.EndsWith(".ass") || path.EndsWith(".ssa") || path.EndsWith(".srt"))
&& (string.Equals(charset, "utf-16le", StringComparison.OrdinalIgnoreCase)
|| string.Equals(charset, "utf-16be", StringComparison.OrdinalIgnoreCase)))
{
diff --git a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
index 2e328ba63..de35acbba 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
@@ -7,14 +7,25 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles
{
+ /// <summary>
+ /// Subtitle writer for the WebVTT format.
+ /// </summary>
public class VttWriter : ISubtitleWriter
{
+ /// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))
{
writer.WriteLine("WEBVTT");
writer.WriteLine(string.Empty);
+ writer.WriteLine("REGION");
+ writer.WriteLine("id:subtitle");
+ writer.WriteLine("width:80%");
+ writer.WriteLine("lines:3");
+ writer.WriteLine("regionanchor:50%,100%");
+ writer.WriteLine("viewportanchor:50%,90%");
+ writer.WriteLine(string.Empty);
foreach (var trackEvent in info.TrackEvents)
{
cancellationToken.ThrowIfCancellationRequested();
@@ -22,13 +33,13 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks);
var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks);
- // make sure the start and end times are different and seqential
+ // make sure the start and end times are different and sequential
if (endTime.TotalMilliseconds <= startTime.TotalMilliseconds)
{
endTime = startTime.Add(TimeSpan.FromMilliseconds(1));
}
- writer.WriteLine(@"{0:hh\:mm\:ss\.fff} --> {1:hh\:mm\:ss\.fff}", startTime, endTime);
+ writer.WriteLine(@"{0:hh\:mm\:ss\.fff} --> {1:hh\:mm\:ss\.fff} region:subtitle", startTime, endTime);
var text = trackEvent.Text;