aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding
diff options
context:
space:
mode:
authorBond-009 <bond.009@outlook.com>2020-06-18 17:01:15 +0200
committerGitHub <noreply@github.com>2020-06-18 17:01:15 +0200
commita3c0b8a826b0f226a4e7a9aa1de6a8cb44dd22d3 (patch)
treed2284787df9647ee49e6aad3fa21fc2ed1d996ea /MediaBrowser.MediaEncoding
parent6b959f40ac208094da0a1d41d8c8a42df9a87876 (diff)
parentfbefddbb816319a77bdf96d1c2216609bef13081 (diff)
Merge branch 'master' into buffer
Diffstat (limited to 'MediaBrowser.MediaEncoding')
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs3
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoExaminer.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs81
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs66
-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.cs58
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssParser.cs10
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtParser.cs5
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs11
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs16
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs15
13 files changed, 210 insertions, 86 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..e6359f4fb 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
{
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index 6e036d24c..4250edfb7 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
@@ -159,6 +183,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 +244,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 c2bfac9d6..1183e9fb2 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -19,14 +19,13 @@ 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;
namespace MediaBrowser.MediaEncoding.Encoder
{
/// <summary>
- /// Class MediaEncoder
+ /// Class MediaEncoder.
/// </summary>
public class MediaEncoder : IMediaEncoder, IDisposable
{
@@ -35,12 +34,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,8 +46,6 @@ 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 _ffprobePath;
@@ -58,23 +54,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
IServerConfigurationManager configurationManager,
IFileSystem fileSystem,
ILocalizationManager localization,
- Func<ISubtitleEncoder> subtitleEncoder,
- IConfiguration configuration,
+ Lazy<EncodingHelper> encodingHelperFactory,
string startupOptionsFFmpegPath)
{
_logger = logger;
_configurationManager = configurationManager;
_fileSystem = fileSystem;
_localization = localization;
+ _encodingHelperFactory = encodingHelperFactory;
_startupOptionFFmpegPath = startupOptionsFFmpegPath;
- _subtitleEncoder = subtitleEncoder;
- _configuration = configuration;
}
- 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 +111,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 +127,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))
{
@@ -189,7 +181,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 +192,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 +224,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 +249,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 +276,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 +439,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
/// <summary>
- /// The us culture
+ /// The us culture.
/// </summary>
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
@@ -478,7 +483,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
}
catch (Exception ex)
{
- _logger.LogError(ex, "I-frame image extraction failed, will attempt standard way. Input: {arguments}", inputArgument);
+ _logger.LogError(ex, "I-frame image extraction failed, will attempt standard way. Input: {Arguments}", inputArgument);
}
}
@@ -509,11 +514,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 +906,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 +934,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
@@ -969,7 +973,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
public int? ExitCode { get; private set; }
- void OnProcessExited(object sender, EventArgs e)
+ private void OnProcessExited(object sender, EventArgs e)
{
var process = (Process)sender;
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..ba807ab85 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();
}
@@ -1004,6 +1023,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist");
}
+
if (string.IsNullOrWhiteSpace(albumArtist))
{
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist");
@@ -1018,7 +1038,6 @@ namespace MediaBrowser.MediaEncoding.Probing
audio.AlbumArtists = SplitArtists(albumArtist, _nameDelimiters, true)
.DistinctNames()
.ToArray();
-
}
if (audio.AlbumArtists.Length == 0)
@@ -1047,23 +1066,23 @@ 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);
+ 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);
+ 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);
+ 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);
+ 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);
+ audio.SetProviderId(MetadataProvider.MusicBrainzTrack, mb);
}
private string GetMultipleMusicBrainzId(string value)
@@ -1147,7 +1166,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 +1187,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
continue;
}
+
if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase))
{
continue;
@@ -1184,7 +1204,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>
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..a8d383a2a 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,6 +53,7 @@ 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);
@@ -65,8 +67,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 +80,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..9a8fcc431 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaParser.cs
@@ -130,11 +130,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;
}
@@ -261,7 +262,6 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text += "</font>";
}
}
-
}
text = text.Replace(@"{\i1}", "<i>");
@@ -303,6 +303,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return count;
index = text.IndexOf(tag, index + 1);
}
+
return count;
}
@@ -330,6 +331,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
rest = string.Empty;
}
+
extraTags += " size=\"" + fontSize.Substring(2) + "\"";
}
else if (rest.StartsWith("fn") && rest.Length > 2)
@@ -345,6 +347,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;