aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding')
-rw-r--r--MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs80
-rw-r--r--MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs2
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs42
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs96
-rw-r--r--MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs69
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs48
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs7
-rw-r--r--MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs7
11 files changed, 185 insertions, 187 deletions
diff --git a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
index 989e386a5..299f294b2 100644
--- a/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
+++ b/MediaBrowser.MediaEncoding/Attachments/AttachmentExtractor.cs
@@ -1,4 +1,3 @@
-#nullable disable
#pragma warning disable CS1591
using System;
@@ -23,7 +22,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Attachments
{
- public class AttachmentExtractor : IAttachmentExtractor, IDisposable
+ public sealed class AttachmentExtractor : IAttachmentExtractor
{
private readonly ILogger<AttachmentExtractor> _logger;
private readonly IApplicationPaths _appPaths;
@@ -34,8 +33,6 @@ namespace MediaBrowser.MediaEncoding.Attachments
private readonly ConcurrentDictionary<string, SemaphoreSlim> _semaphoreLocks =
new ConcurrentDictionary<string, SemaphoreSlim>();
- private bool _disposed = false;
-
public AttachmentExtractor(
ILogger<AttachmentExtractor> logger,
IApplicationPaths appPaths,
@@ -177,22 +174,16 @@ namespace MediaBrowser.MediaEncoding.Attachments
process.Start();
- var ranToCompletion = await ProcessExtensions.WaitForExitAsync(process, cancellationToken).ConfigureAwait(false);
-
- if (!ranToCompletion)
+ try
{
- try
- {
- _logger.LogWarning("Killing ffmpeg attachment extraction process");
- process.Kill();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error killing attachment extraction process");
- }
+ await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
+ exitCode = process.ExitCode;
+ }
+ catch (OperationCanceledException)
+ {
+ process.Kill(true);
+ exitCode = -1;
}
-
- exitCode = ranToCompletion ? process.ExitCode : -1;
}
var failed = false;
@@ -296,7 +287,7 @@ namespace MediaBrowser.MediaEncoding.Attachments
ArgumentException.ThrowIfNullOrEmpty(outputPath);
- Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
+ Directory.CreateDirectory(Path.GetDirectoryName(outputPath) ?? throw new ArgumentException("Path can't be a root directory.", nameof(outputPath)));
var processArgs = string.Format(
CultureInfo.InvariantCulture,
@@ -325,22 +316,16 @@ namespace MediaBrowser.MediaEncoding.Attachments
process.Start();
- var ranToCompletion = await ProcessExtensions.WaitForExitAsync(process, cancellationToken).ConfigureAwait(false);
-
- if (!ranToCompletion)
+ try
{
- try
- {
- _logger.LogWarning("Killing ffmpeg attachment extraction process");
- process.Kill();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error killing attachment extraction process");
- }
+ await process.WaitForExitAsync(cancellationToken).ConfigureAwait(false);
+ exitCode = process.ExitCode;
+ }
+ catch (OperationCanceledException)
+ {
+ process.Kill(true);
+ exitCode = -1;
}
-
- exitCode = ranToCompletion ? process.ExitCode : -1;
}
var failed = false;
@@ -391,33 +376,8 @@ namespace MediaBrowser.MediaEncoding.Attachments
filename = (mediaPath + attachmentStreamIndex.ToString(CultureInfo.InvariantCulture)).GetMD5().ToString("D", CultureInfo.InvariantCulture);
}
- var prefix = filename.Substring(0, 1);
- return Path.Combine(_appPaths.DataPath, "attachments", prefix, filename);
- }
-
- /// <inheritdoc />
- public void Dispose()
- {
- Dispose(true);
- GC.SuppressFinalize(this);
- }
-
- /// <summary>
- /// Releases unmanaged and - optionally - managed resources.
- /// </summary>
- /// <param name="disposing"><c>true</c> to release both managed and unmanaged resources; <c>false</c> to release only unmanaged resources.</param>
- protected virtual void Dispose(bool disposing)
- {
- if (_disposed)
- {
- return;
- }
-
- if (disposing)
- {
- }
-
- _disposed = true;
+ var prefix = filename.AsSpan(0, 1);
+ return Path.Join(_appPaths.DataPath, "attachments", prefix, filename);
}
}
}
diff --git a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs
index 9e7a1d50a..1f94d9b23 100644
--- a/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs
+++ b/MediaBrowser.MediaEncoding/BdInfo/BdInfoFileInfo.cs
@@ -8,7 +8,7 @@ namespace MediaBrowser.MediaEncoding.BdInfo;
/// </summary>
public class BdInfoFileInfo : BDInfo.IO.IFileInfo
{
- private FileSystemMetadata _impl;
+ private readonly FileSystemMetadata _impl;
/// <summary>
/// Initializes a new instance of the <see cref="BdInfoFileInfo" /> class.
diff --git a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
index e1a0e8d67..f12ef7e63 100644
--- a/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/EncoderValidator.cs
@@ -10,7 +10,7 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Encoder
{
- public class EncoderValidator
+ public partial class EncoderValidator
{
private static readonly string[] _requiredDecoders = new[]
{
@@ -165,6 +165,12 @@ namespace MediaBrowser.MediaEncoding.Encoder
public static Version? MaxVersion { get; } = null;
+ [GeneratedRegex(@"^ffmpeg version n?((?:[0-9]+\.?)+)")]
+ private static partial Regex FfmpegVersionRegex();
+
+ [GeneratedRegex(@"((?<name>lib\w+)\s+(?<major>[0-9]+)\.\s*(?<minor>[0-9]+))", RegexOptions.Multiline)]
+ private static partial Regex LibraryRegex();
+
public bool ValidateVersion()
{
string output;
@@ -283,7 +289,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
internal Version? GetFFmpegVersionInternal(string output)
{
// For pre-built binaries the FFmpeg version should be mentioned at the very start of the output
- var match = Regex.Match(output, @"^ffmpeg version n?((?:[0-9]+\.?)+)");
+ var match = FfmpegVersionRegex().Match(output);
if (match.Success)
{
@@ -331,10 +337,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var map = new Dictionary<string, Version>();
- foreach (Match match in Regex.Matches(
- output,
- @"((?<name>lib\w+)\s+(?<major>[0-9]+)\.\s*(?<minor>[0-9]+))",
- RegexOptions.Multiline))
+ foreach (Match match in LibraryRegex().Matches(output))
{
var version = new Version(
int.Parse(match.Groups["major"].ValueSpan, CultureInfo.InvariantCulture),
@@ -496,8 +499,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
var required = codec == Codec.Encoder ? _requiredEncoders : _requiredDecoders;
- var found = Regex
- .Matches(output, @"^\s\S{6}\s(?<codec>[\w|-]+)\s+.+$", RegexOptions.Multiline)
+ var found = CodecRegex()
+ .Matches(output)
.Select(x => x.Groups["codec"].Value)
.Where(x => required.Contains(x));
@@ -524,8 +527,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
return Enumerable.Empty<string>();
}
- var found = Regex
- .Matches(output, @"^\s\S{3}\s(?<filter>[\w|-]+)\s+.+$", RegexOptions.Multiline)
+ var found = FilterRegex()
+ .Matches(output)
.Select(x => x.Groups["filter"].Value)
.Where(x => _requiredFilters.Contains(x));
@@ -550,7 +553,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetProcessOutput(string path, string arguments, bool readStdErr, string? testKey)
{
- using (var process = new Process()
+ var redirectStandardIn = !string.IsNullOrEmpty(testKey);
+ using (var process = new Process
{
StartInfo = new ProcessStartInfo(path, arguments)
{
@@ -558,7 +562,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
UseShellExecute = false,
WindowStyle = ProcessWindowStyle.Hidden,
ErrorDialog = false,
- RedirectStandardInput = !string.IsNullOrEmpty(testKey),
+ RedirectStandardInput = redirectStandardIn,
RedirectStandardOutput = true,
RedirectStandardError = true
}
@@ -568,13 +572,21 @@ namespace MediaBrowser.MediaEncoding.Encoder
process.Start();
- if (!string.IsNullOrEmpty(testKey))
+ if (redirectStandardIn)
{
- process.StandardInput.Write(testKey);
+ using var writer = process.StandardInput;
+ writer.Write(testKey);
}
- return readStdErr ? process.StandardError.ReadToEnd() : process.StandardOutput.ReadToEnd();
+ using var reader = readStdErr ? process.StandardError : process.StandardOutput;
+ return reader.ReadToEnd();
}
}
+
+ [GeneratedRegex("^\\s\\S{6}\\s(?<codec>[\\w|-]+)\\s+.+$", RegexOptions.Multiline)]
+ private static partial Regex CodecRegex();
+
+ [GeneratedRegex("^\\s\\S{3}\\s(?<filter>[\\w|-]+)\\s+.+$", RegexOptions.Multiline)]
+ private static partial Regex FilterRegex();
}
}
diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
index fa695bbdc..0b603715d 100644
--- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
@@ -39,7 +39,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
/// <summary>
/// Class MediaEncoder.
/// </summary>
- public class MediaEncoder : IMediaEncoder, IDisposable
+ public partial class MediaEncoder : IMediaEncoder, IDisposable
{
/// <summary>
/// The default SDR image extraction timeout in milliseconds.
@@ -79,12 +79,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
private bool _isVaapiDeviceAmd = false;
private bool _isVaapiDeviceInteliHD = false;
private bool _isVaapiDeviceInteli965 = false;
- private bool _isVaapiDeviceSupportVulkanFmtModifier = false;
+ private bool _isVaapiDeviceSupportVulkanDrmInterop = false;
- private static string[] _vulkanFmtModifierExts =
+ private static string[] _vulkanExternalMemoryDmaBufExts =
{
- "VK_KHR_sampler_ycbcr_conversion",
- "VK_EXT_image_drm_format_modifier",
"VK_KHR_external_memory_fd",
"VK_EXT_external_memory_dma_buf",
"VK_KHR_external_semaphore_fd",
@@ -143,7 +141,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
public bool IsVaapiDeviceInteli965 => _isVaapiDeviceInteli965;
/// <inheritdoc />
- public bool IsVaapiDeviceSupportVulkanFmtModifier => _isVaapiDeviceSupportVulkanFmtModifier;
+ public bool IsVaapiDeviceSupportVulkanDrmInterop => _isVaapiDeviceSupportVulkanDrmInterop;
+
+ [GeneratedRegex(@"[^\/\\]+?(\.[^\/\\\n.]+)?$")]
+ private static partial Regex FfprobePathRegex();
/// <summary>
/// Run at startup or if the user removes a Custom path from transcode page.
@@ -179,7 +180,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
if (_ffmpegPath is not null)
{
// Determine a probe path from the mpeg path
- _ffprobePath = Regex.Replace(_ffmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1");
+ _ffprobePath = FfprobePathRegex().Replace(_ffmpegPath, "ffprobe$1");
// Interrogate to understand what coders are supported
var validator = new EncoderValidator(_logger, _ffmpegPath);
@@ -204,7 +205,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_isVaapiDeviceAmd = validator.CheckVaapiDeviceByDriverName("Mesa Gallium driver", options.VaapiDevice);
_isVaapiDeviceInteliHD = validator.CheckVaapiDeviceByDriverName("Intel iHD driver", options.VaapiDevice);
_isVaapiDeviceInteli965 = validator.CheckVaapiDeviceByDriverName("Intel i965 driver", options.VaapiDevice);
- _isVaapiDeviceSupportVulkanFmtModifier = validator.CheckVulkanDrmDeviceByExtensionName(options.VaapiDevice, _vulkanFmtModifierExts);
+ _isVaapiDeviceSupportVulkanDrmInterop = validator.CheckVulkanDrmDeviceByExtensionName(options.VaapiDevice, _vulkanExternalMemoryDmaBufExts);
if (_isVaapiDeviceAmd)
{
@@ -219,9 +220,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
_logger.LogInformation("VAAPI device {RenderNodePath} is Intel GPU (i965)", options.VaapiDevice);
}
- if (_isVaapiDeviceSupportVulkanFmtModifier)
+ if (_isVaapiDeviceSupportVulkanDrmInterop)
{
- _logger.LogInformation("VAAPI device {RenderNodePath} supports Vulkan DRM format modifier", options.VaapiDevice);
+ _logger.LogInformation("VAAPI device {RenderNodePath} supports Vulkan DRM interop", options.VaapiDevice);
}
}
}
@@ -318,10 +319,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
var files = _fileSystem.GetFilePaths(path, recursive);
- var excludeExtensions = new[] { ".c" };
-
- return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), filename, StringComparison.OrdinalIgnoreCase)
- && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
+ return files.FirstOrDefault(i => Path.GetFileNameWithoutExtension(i.AsSpan()).Equals(filename, StringComparison.OrdinalIgnoreCase)
+ && !Path.GetExtension(i.AsSpan()).Equals(".c", StringComparison.OrdinalIgnoreCase));
}
catch (Exception)
{
@@ -419,24 +418,36 @@ namespace MediaBrowser.MediaEncoding.Encoder
public Task<MediaInfo> GetMediaInfo(MediaInfoRequest request, CancellationToken cancellationToken)
{
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
- string analyzeDuration = string.Empty;
- string ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
+ var analyzeDuration = string.Empty;
+ var ffmpegAnalyzeDuration = _config.GetFFmpegAnalyzeDuration() ?? string.Empty;
+ var ffmpegProbeSize = _config.GetFFmpegProbeSize() ?? string.Empty;
+ var extraArgs = string.Empty;
if (request.MediaSource.AnalyzeDurationMs > 0)
{
- analyzeDuration = "-analyzeduration " + (request.MediaSource.AnalyzeDurationMs * 1000).ToString();
+ analyzeDuration = "-analyzeduration " + (request.MediaSource.AnalyzeDurationMs * 1000);
}
else if (!string.IsNullOrEmpty(ffmpegAnalyzeDuration))
{
analyzeDuration = "-analyzeduration " + ffmpegAnalyzeDuration;
}
+ if (!string.IsNullOrEmpty(analyzeDuration))
+ {
+ extraArgs = analyzeDuration;
+ }
+
+ if (!string.IsNullOrEmpty(ffmpegProbeSize))
+ {
+ extraArgs += " -probesize " + ffmpegProbeSize;
+ }
+
return GetMediaInfoInternal(
GetInputArgument(request.MediaSource.Path, request.MediaSource),
request.MediaSource.Path,
request.MediaSource.Protocol,
extractChapters,
- analyzeDuration,
+ extraArgs,
request.MediaType == DlnaProfileType.Audio,
request.MediaSource.VideoType,
cancellationToken);
@@ -513,7 +524,8 @@ namespace MediaBrowser.MediaEncoding.Encoder
using (var processWrapper = new ProcessWrapper(process, this))
{
StartProcess(processWrapper);
- await process.StandardOutput.BaseStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
+ using var reader = process.StandardOutput;
+ await reader.BaseStream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false);
memoryStream.Seek(0, SeekOrigin.Begin);
InternalMediaInfoResult result;
try
@@ -614,9 +626,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
private string GetImageResolutionParameter()
{
- string imageResolutionParameter;
-
- imageResolutionParameter = _serverConfig.Configuration.ChapterImageResolution switch
+ var imageResolutionParameter = _serverConfig.Configuration.ChapterImageResolution switch
{
ImageResolution.P144 => "256x144",
ImageResolution.P240 => "426x240",
@@ -641,15 +651,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
ArgumentException.ThrowIfNullOrEmpty(inputPath);
- var outputExtension = targetFormat switch
- {
- ImageFormat.Bmp => ".bmp",
- ImageFormat.Gif => ".gif",
- ImageFormat.Jpg => ".jpg",
- ImageFormat.Png => ".png",
- ImageFormat.Webp => ".webp",
- _ => ".jpg"
- };
+ var outputExtension = targetFormat?.GetExtension() ?? ".jpg";
var tempExtractPath = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + outputExtension);
Directory.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
@@ -669,13 +671,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
var scaler = threedFormat switch
{
// hsbs crop width in half,scale to correct size, set the display aspect,crop out any black bars we may have made. Work out the correct height based on the display aspect it will maintain the aspect where -1 in this case (3d) may not.
- Video3DFormat.HalfSideBySide => "crop=iw/2:ih: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",
+ Video3DFormat.HalfSideBySide => @"crop=iw/2:ih: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",
// fsbs crop width in half,set the display aspect,crop out any black bars we may have made
- Video3DFormat.FullSideBySide => "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",
+ Video3DFormat.FullSideBySide => @"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",
// htab crop height in half,scale to correct size, set the display aspect,crop out any black bars we may have made
- Video3DFormat.HalfTopAndBottom => "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",
+ Video3DFormat.HalfTopAndBottom => @"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",
// ftab crop height in half, set the display aspect,crop out any black bars we may have made
- Video3DFormat.FullTopAndBottom => "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",
+ Video3DFormat.FullTopAndBottom => @"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=trunc(iw*sar):ih"
};
@@ -751,11 +753,15 @@ namespace MediaBrowser.MediaEncoding.Encoder
timeoutMs = enableHdrExtraction ? DefaultHdrImageExtractionTimeout : DefaultSdrImageExtractionTimeout;
}
- ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
-
- if (!ranToCompletion)
+ try
{
- StopProcess(processWrapper, 1000);
+ await process.WaitForExitAsync(TimeSpan.FromMilliseconds(timeoutMs)).ConfigureAwait(false);
+ ranToCompletion = true;
+ }
+ catch (OperationCanceledException)
+ {
+ process.Kill(true);
+ ranToCompletion = false;
}
}
finally
@@ -1024,7 +1030,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
// https://ffmpeg.org/ffmpeg-filters.html#Notes-on-filtergraph-escaping
// We need to double escape
- return path.Replace('\\', '/').Replace(":", "\\:", StringComparison.Ordinal).Replace("'", "'\\\\\\''", StringComparison.Ordinal);
+ return path.Replace('\\', '/').Replace(":", "\\:", StringComparison.Ordinal).Replace("'", @"'\\\''", StringComparison.Ordinal);
}
/// <inheritdoc />
@@ -1167,7 +1173,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
return true;
}
- private class ProcessWrapper : IDisposable
+ private sealed class ProcessWrapper : IDisposable
{
private readonly MediaEncoder _mediaEncoder;
@@ -1210,13 +1216,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_mediaEncoder._runningProcesses.Remove(this);
}
- try
- {
- process.Dispose();
- }
- catch
- {
- }
+ process.Dispose();
}
public void Dispose()
diff --git a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
index 7d655240b..be1e8a172 100644
--- a/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
+++ b/MediaBrowser.MediaEncoding/Probing/ProbeResultNormalizer.cs
@@ -1,5 +1,4 @@
#nullable disable
-#pragma warning disable CS1591
using System;
using System.Collections.Generic;
@@ -20,7 +19,10 @@ using Microsoft.Extensions.Logging;
namespace MediaBrowser.MediaEncoding.Probing
{
- public class ProbeResultNormalizer
+ /// <summary>
+ /// Class responsible for normalizing FFprobe output.
+ /// </summary>
+ public partial class ProbeResultNormalizer
{
// When extracting subtitles, the maximum length to consider (to avoid invalid filenames)
private const int MaxSubtitleDescriptionExtractionLength = 100;
@@ -29,13 +31,16 @@ namespace MediaBrowser.MediaEncoding.Probing
private readonly char[] _nameDelimiters = { '/', '|', ';', '\\' };
- private static readonly Regex _performerPattern = new(@"(?<name>.*) \((?<instrument>.*)\)");
-
private readonly ILogger _logger;
private readonly ILocalizationManager _localization;
private string[] _splitWhiteList;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ProbeResultNormalizer"/> class.
+ /// </summary>
+ /// <param name="logger">The <see cref="ILogger{ProbeResultNormalizer}"/> for use with the <see cref="ProbeResultNormalizer"/> instance.</param>
+ /// <param name="localization">The <see cref="ILocalizationManager"/> for use with the <see cref="ProbeResultNormalizer"/> instance.</param>
public ProbeResultNormalizer(ILogger logger, ILocalizationManager localization)
{
_logger = logger;
@@ -71,8 +76,18 @@ namespace MediaBrowser.MediaEncoding.Probing
"She/Her/Hers",
"5/8erl in Ehr'n",
"Smith/Kotzen",
+ "We;Na",
};
+ /// <summary>
+ /// Transforms a FFprobe response into its <see cref="MediaInfo"/> equivalent.
+ /// </summary>
+ /// <param name="data">The <see cref="InternalMediaInfoResult"/>.</param>
+ /// <param name="videoType">The <see cref="VideoType"/>.</param>
+ /// <param name="isAudio">A boolean indicating whether the media is audio.</param>
+ /// <param name="path">Path to media file.</param>
+ /// <param name="protocol">Path media protocol.</param>
+ /// <returns>The <see cref="MediaInfo"/>.</returns>
public MediaInfo GetMediaInfo(InternalMediaInfoResult data, VideoType? videoType, bool isAudio, string path, MediaProtocol protocol)
{
var info = new MediaInfo
@@ -252,25 +267,30 @@ namespace MediaBrowser.MediaEncoding.Probing
return null;
}
- // Handle MPEG-1 container
- if (string.Equals(format, "mpegvideo", StringComparison.OrdinalIgnoreCase))
+ // Input can be a list of multiple, comma-delimited formats - each of them needs to be checked
+ var splitFormat = format.Split(',');
+ for (var i = 0; i < splitFormat.Length; i++)
{
- return "mpeg";
- }
+ // Handle MPEG-1 container
+ if (string.Equals(splitFormat[i], "mpegvideo", StringComparison.OrdinalIgnoreCase))
+ {
+ splitFormat[i] = "mpeg";
+ }
- // Handle MPEG-2 container
- if (string.Equals(format, "mpeg", StringComparison.OrdinalIgnoreCase))
- {
- return "ts";
- }
+ // Handle MPEG-2 container
+ else if (string.Equals(splitFormat[i], "mpeg", StringComparison.OrdinalIgnoreCase))
+ {
+ splitFormat[i] = "ts";
+ }
- // Handle matroska container
- if (string.Equals(format, "matroska", StringComparison.OrdinalIgnoreCase))
- {
- return "mkv";
+ // Handle matroska container
+ else if (string.Equals(splitFormat[i], "matroska", StringComparison.OrdinalIgnoreCase))
+ {
+ splitFormat[i] = "mkv";
+ }
}
- return format;
+ return string.Join(',', splitFormat);
}
private int? GetEstimatedAudioBitrate(string codec, int? channels)
@@ -741,9 +761,11 @@ namespace MediaBrowser.MediaEncoding.Probing
&& !string.Equals(streamInfo.FieldOrder, "progressive", StringComparison.OrdinalIgnoreCase);
if (isAudio
- || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
- || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)
- || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase))
+ && (string.Equals(stream.Codec, "bmp", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(stream.Codec, "gif", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(stream.Codec, "mjpeg", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(stream.Codec, "png", StringComparison.OrdinalIgnoreCase)
+ || string.Equals(stream.Codec, "webp", StringComparison.OrdinalIgnoreCase)))
{
stream.Type = MediaStreamType.EmbeddedImage;
}
@@ -1191,7 +1213,7 @@ namespace MediaBrowser.MediaEncoding.Probing
{
foreach (var person in Split(performer, false))
{
- Match match = _performerPattern.Match(person);
+ Match match = PerformerRegex().Match(person);
// If the performer doesn't have any instrument/role associated, it won't match. In that case, chances are it's simply a band name, so we skip it.
if (match.Success)
@@ -1630,5 +1652,8 @@ namespace MediaBrowser.MediaEncoding.Probing
return TransportStreamTimestamp.Valid;
}
+
+ [GeneratedRegex("(?<name>.*) \\((?<instrument>.*)\\)")]
+ private static partial Regex PerformerRegex();
}
}
diff --git a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
index 0d1cf6e25..7d7b80e99 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/AssWriter.cs
@@ -11,8 +11,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// ASS subtitle writer.
/// </summary>
- public class AssWriter : ISubtitleWriter
+ public partial class AssWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -40,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var trackEvent = trackEvents[i];
var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
- var text = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase);
+ var text = NewLineRegex().Replace(trackEvent.Text, "\\n");
writer.WriteLine(
"Dialogue: 0,{0},{1},Default,{2}",
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
index 143c010b7..86f77aa06 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SrtWriter.cs
@@ -11,8 +11,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// SRT subtitle writer.
/// </summary>
- public class SrtWriter : ISubtitleWriter
+ public partial class SrtWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineEscapedRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -35,7 +38,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var text = trackEvent.Text;
// TODO: Not sure how to handle these
- text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
+ text = NewLineEscapedRegex().Replace(text, " ");
writer.WriteLine(text);
writer.WriteLine();
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
index 6761cd309..b5fd1ed93 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SsaWriter.cs
@@ -11,8 +11,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// SSA subtitle writer.
/// </summary>
- public class SsaWriter : ISubtitleWriter
+ public partial class SsaWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -40,7 +43,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var trackEvent = trackEvents[i];
var startTime = TimeSpan.FromTicks(trackEvent.StartPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
var endTime = TimeSpan.FromTicks(trackEvent.EndPositionTicks).ToString(timeFormat, CultureInfo.InvariantCulture);
- var text = Regex.Replace(trackEvent.Text, @"\n", "\\n", RegexOptions.IgnoreCase);
+ var text = NewLineRegex().Replace(trackEvent.Text, "\\n");
writer.WriteLine(
"Dialogue: 0,{0},{1},Default,{2}",
diff --git a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
index 794906c3b..21fa4468e 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/SubtitleEncoder.cs
@@ -293,7 +293,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return true;
}
- if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase))
+ if (string.Equals(format, SubtitleFormat.VTT, StringComparison.OrdinalIgnoreCase) || string.Equals(format, SubtitleFormat.WEBVTT, StringComparison.OrdinalIgnoreCase))
{
value = new VttWriter();
return true;
@@ -420,23 +420,16 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw;
}
- var ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMinutes(30)).ConfigureAwait(false);
-
- if (!ranToCompletion)
+ try
{
- try
- {
- _logger.LogInformation("Killing ffmpeg subtitle conversion process");
-
- process.Kill();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error killing subtitle conversion process");
- }
+ await process.WaitForExitAsync(TimeSpan.FromMinutes(30)).ConfigureAwait(false);
+ exitCode = process.ExitCode;
+ }
+ catch (OperationCanceledException)
+ {
+ process.Kill(true);
+ exitCode = -1;
}
-
- exitCode = ranToCompletion ? process.ExitCode : -1;
}
var failed = false;
@@ -574,23 +567,16 @@ namespace MediaBrowser.MediaEncoding.Subtitles
throw;
}
- var ranToCompletion = await process.WaitForExitAsync(TimeSpan.FromMinutes(30)).ConfigureAwait(false);
-
- if (!ranToCompletion)
+ try
{
- try
- {
- _logger.LogWarning("Killing ffmpeg subtitle extraction process");
-
- process.Kill();
- }
- catch (Exception ex)
- {
- _logger.LogError(ex, "Error killing subtitle extraction process");
- }
+ await process.WaitForExitAsync(TimeSpan.FromMinutes(30)).ConfigureAwait(false);
+ exitCode = process.ExitCode;
+ }
+ catch (OperationCanceledException)
+ {
+ process.Kill(true);
+ exitCode = -1;
}
-
- exitCode = ranToCompletion ? process.ExitCode : -1;
}
var failed = false;
diff --git a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
index e5c785bc5..ea45f2070 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/TtmlWriter.cs
@@ -9,8 +9,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// TTML subtitle writer.
/// </summary>
- public class TtmlWriter : ISubtitleWriter
+ public partial class TtmlWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewLineEscapeRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -38,7 +41,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{
var text = trackEvent.Text;
- text = Regex.Replace(text, @"\\n", "<br/>", RegexOptions.IgnoreCase);
+ text = NewLineEscapeRegex().Replace(text, "<br/>");
writer.WriteLine(
"<p begin=\"{0}\" dur=\"{1}\">{2}</p>",
diff --git a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
index 38ef57dee..3e0f47b5a 100644
--- a/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
+++ b/MediaBrowser.MediaEncoding/Subtitles/VttWriter.cs
@@ -10,8 +10,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <summary>
/// Subtitle writer for the WebVTT format.
/// </summary>
- public class VttWriter : ISubtitleWriter
+ public partial class VttWriter : ISubtitleWriter
{
+ [GeneratedRegex(@"\\n", RegexOptions.IgnoreCase)]
+ private static partial Regex NewlineEscapeRegex();
+
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{
@@ -39,7 +42,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
var text = trackEvent.Text;
// TODO: Not sure how to handle these
- text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
+ text = NewlineEscapeRegex().Replace(text, " ");
writer.WriteLine(text);
writer.WriteLine();