Review usage of string.Substring (part 1)

Reduced allocations by replacing string.Substring with ReadOnlySpan<char>.Slice
This commit is contained in:
Bond_009 2020-07-22 13:34:51 +02:00
parent 0750357916
commit febb6bced6
25 changed files with 124 additions and 99 deletions

View File

@ -364,7 +364,8 @@ namespace Emby.Dlna.Didl
writer.WriteAttributeString("bitrate", totalBitrate.Value.ToString(_usCulture)); writer.WriteAttributeString("bitrate", totalBitrate.Value.ToString(_usCulture));
} }
var mediaProfile = _profile.GetVideoMediaProfile(streamInfo.Container, var mediaProfile = _profile.GetVideoMediaProfile(
streamInfo.Container,
streamInfo.TargetAudioCodec.FirstOrDefault(), streamInfo.TargetAudioCodec.FirstOrDefault(),
streamInfo.TargetVideoCodec.FirstOrDefault(), streamInfo.TargetVideoCodec.FirstOrDefault(),
streamInfo.TargetAudioBitrate, streamInfo.TargetAudioBitrate,

View File

@ -387,7 +387,7 @@ namespace Emby.Dlna
foreach (var name in _assembly.GetManifestResourceNames()) foreach (var name in _assembly.GetManifestResourceNames())
{ {
if (!name.StartsWith(namespaceName)) if (!name.StartsWith(namespaceName, StringComparison.Ordinal))
{ {
continue; continue;
} }
@ -406,7 +406,7 @@ namespace Emby.Dlna
using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var fileStream = new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.Read))
{ {
await stream.CopyToAsync(fileStream); await stream.CopyToAsync(fileStream).ConfigureAwait(false);
} }
} }
} }
@ -509,7 +509,7 @@ namespace Emby.Dlna
return _jsonSerializer.DeserializeFromString<DeviceProfile>(json); return _jsonSerializer.DeserializeFromString<DeviceProfile>(json);
} }
class InternalProfileInfo private class InternalProfileInfo
{ {
internal DeviceProfileInfo Info { get; set; } internal DeviceProfileInfo Info { get; set; }

View File

@ -448,21 +448,21 @@ namespace Emby.Drawing
/// or /// or
/// filename. /// filename.
/// </exception> /// </exception>
public string GetCachePath(string path, string filename) public string GetCachePath(ReadOnlySpan<char> path, ReadOnlySpan<char> filename)
{ {
if (string.IsNullOrEmpty(path)) if (path.IsEmpty)
{ {
throw new ArgumentNullException(nameof(path)); throw new ArgumentException("Path can't be empty.", nameof(path));
} }
if (string.IsNullOrEmpty(filename)) if (path.IsEmpty)
{ {
throw new ArgumentNullException(nameof(filename)); throw new ArgumentException("Filename can't be empty.", nameof(filename));
} }
var prefix = filename.Substring(0, 1); var prefix = filename.Slice(0, 1);
return Path.Combine(path, prefix, filename); return Path.Join(path, prefix, filename);
} }
/// <inheritdoc /> /// <inheritdoc />

View File

@ -77,7 +77,7 @@ namespace Emby.Naming.TV
if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase)) if (filename.StartsWith("s", StringComparison.OrdinalIgnoreCase))
{ {
var testFilename = filename.Substring(1); var testFilename = filename.AsSpan().Slice(1);
if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val)) if (int.TryParse(testFilename, NumberStyles.Integer, CultureInfo.InvariantCulture, out var val))
{ {

View File

@ -511,8 +511,8 @@ namespace Emby.Server.Implementations.Library
{ {
// Try to normalize paths located underneath program-data in an attempt to make them more portable // Try to normalize paths located underneath program-data in an attempt to make them more portable
key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length) key = key.Substring(_configurationManager.ApplicationPaths.ProgramDataPath.Length)
.TrimStart(new[] { '/', '\\' }) .TrimStart('/', '\\')
.Replace("/", "\\"); .Replace('/', '\\');
} }
if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds) if (forceCaseInsensitive || !_configurationManager.Configuration.EnableCaseSensitiveItemIds)

View File

@ -869,7 +869,7 @@ namespace Emby.Server.Implementations.Library
var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase)); var provider = _providers.FirstOrDefault(i => string.Equals(i.GetType().FullName.GetMD5().ToString("N", CultureInfo.InvariantCulture), keys[0], StringComparison.OrdinalIgnoreCase));
var splitIndex = key.IndexOf(LiveStreamIdDelimeter); var splitIndex = key.IndexOf(LiveStreamIdDelimeter, StringComparison.Ordinal);
var keyId = key.Substring(splitIndex + 1); var keyId = key.Substring(splitIndex + 1);
return new Tuple<IMediaSourceProvider, string>(provider, keyId); return new Tuple<IMediaSourceProvider, string>(provider, keyId);
@ -881,6 +881,7 @@ namespace Emby.Server.Implementations.Library
public void Dispose() public void Dispose()
{ {
Dispose(true); Dispose(true);
GC.SuppressFinalize(this);
} }
private readonly object _disposeLock = new object(); private readonly object _disposeLock = new object();

View File

@ -461,7 +461,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms( private async Task<List<ScheduleDirect.ShowImages>> GetImageForPrograms(
ListingsProviderInfo info, ListingsProviderInfo info,
List<string> programIds, List<string> programIds,
CancellationToken cancellationToken) CancellationToken cancellationToken)
{ {
if (programIds.Count == 0) if (programIds.Count == 0)
{ {
@ -474,7 +474,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings
{ {
var imageId = i.Substring(0, 10); var imageId = i.Substring(0, 10);
if (!imageIdString.Contains(imageId)) if (!imageIdString.Contains(imageId, StringComparison.Ordinal))
{ {
imageIdString += "\"" + imageId + "\","; imageIdString += "\"" + imageId + "\",";
} }

View File

@ -195,7 +195,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
while (!sr.EndOfStream) while (!sr.EndOfStream)
{ {
string line = StripXML(sr.ReadLine()); string line = StripXML(sr.ReadLine());
if (line.Contains("Channel")) if (line.Contains("Channel", StringComparison.Ordinal))
{ {
LiveTvTunerStatus status; LiveTvTunerStatus status;
var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase); var index = line.IndexOf("Channel", StringComparison.OrdinalIgnoreCase);
@ -226,6 +226,11 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
private static string StripXML(string source) private static string StripXML(string source)
{ {
if (string.IsNullOrEmpty(source))
{
return string.Empty;
}
char[] buffer = new char[source.Length]; char[] buffer = new char[source.Length];
int bufferIndex = 0; int bufferIndex = 0;
bool inside = false; bool inside = false;
@ -270,7 +275,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun
for (int i = 0; i < model.TunerCount; ++i) for (int i = 0; i < model.TunerCount; ++i)
{ {
var name = string.Format("Tuner {0}", i + 1); var name = string.Format(CultureInfo.InvariantCulture, "Tuner {0}", i + 1);
var currentChannel = "none"; // @todo Get current channel and map back to Station Id var currentChannel = "none"; // @todo Get current channel and map back to Station Id
var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false); var isAvailable = await manager.CheckTunerAvailability(ipInfo, i, cancellationToken).ConfigureAwait(false);
var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv; var status = isAvailable ? LiveTvTunerStatus.Available : LiveTvTunerStatus.LiveTv;

View File

@ -158,15 +158,14 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
private string GetChannelNumber(string extInf, Dictionary<string, string> attributes, string mediaUrl) private string GetChannelNumber(string extInf, Dictionary<string, string> attributes, string mediaUrl)
{ {
var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); var nameParts = extInf.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
var nameInExtInf = nameParts.Length > 1 ? nameParts[nameParts.Length - 1].Trim() : null; var nameInExtInf = nameParts.Length > 1 ? nameParts[^1].AsSpan().Trim() : ReadOnlySpan<char>.Empty;
string numberString = null; string numberString = null;
string attributeValue; string attributeValue;
double doubleValue;
if (attributes.TryGetValue("tvg-chno", out attributeValue)) if (attributes.TryGetValue("tvg-chno", out attributeValue))
{ {
if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue)) if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{ {
numberString = attributeValue; numberString = attributeValue;
} }
@ -176,36 +175,36 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
if (attributes.TryGetValue("tvg-id", out attributeValue)) if (attributes.TryGetValue("tvg-id", out attributeValue))
{ {
if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue)) if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{ {
numberString = attributeValue; numberString = attributeValue;
} }
else if (attributes.TryGetValue("channel-id", out attributeValue)) else if (attributes.TryGetValue("channel-id", out attributeValue))
{ {
if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out doubleValue)) if (double.TryParse(attributeValue, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{ {
numberString = attributeValue; numberString = attributeValue;
} }
} }
} }
if (String.IsNullOrWhiteSpace(numberString)) if (string.IsNullOrWhiteSpace(numberString))
{ {
// Using this as a fallback now as this leads to Problems with channels like "5 USA" // Using this as a fallback now as this leads to Problems with channels like "5 USA"
// where 5 isnt ment to be the channel number // where 5 isnt ment to be the channel number
// Check for channel number with the format from SatIp // Check for channel number with the format from SatIp
// #EXTINF:0,84. VOX Schweiz // #EXTINF:0,84. VOX Schweiz
// #EXTINF:0,84.0 - VOX Schweiz // #EXTINF:0,84.0 - VOX Schweiz
if (!string.IsNullOrWhiteSpace(nameInExtInf)) if (!nameInExtInf.IsEmpty && !nameInExtInf.IsWhiteSpace())
{ {
var numberIndex = nameInExtInf.IndexOf(' '); var numberIndex = nameInExtInf.IndexOf(' ');
if (numberIndex > 0) if (numberIndex > 0)
{ {
var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' }); var numberPart = nameInExtInf.Slice(0, numberIndex).Trim(new[] { ' ', '.' });
if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number)) if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{ {
numberString = numberPart; numberString = numberPart.ToString();
} }
} }
} }
@ -231,7 +230,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
try try
{ {
numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/').Last()); numberString = Path.GetFileNameWithoutExtension(mediaUrl.Split('/')[^1]);
if (!IsValidChannelNumber(numberString)) if (!IsValidChannelNumber(numberString))
{ {
@ -258,7 +257,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
return false; return false;
} }
if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out var value)) if (!double.TryParse(numberString, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{ {
return false; return false;
} }
@ -281,7 +280,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts
{ {
var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' }); var numberPart = nameInExtInf.Substring(0, numberIndex).Trim(new[] { ' ', '.' });
if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out var number)) if (double.TryParse(numberPart, NumberStyles.Any, CultureInfo.InvariantCulture, out _))
{ {
// channel.Number = number.ToString(); // channel.Number = number.ToString();
nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' }); nameInExtInf = nameInExtInf.Substring(numberIndex + 1).Trim(new[] { ' ', '-' });

View File

@ -247,7 +247,7 @@ namespace Emby.Server.Implementations.Localization
} }
// Try splitting by : to handle "Germany: FSK 18" // Try splitting by : to handle "Germany: FSK 18"
var index = rating.IndexOf(':'); var index = rating.IndexOf(':', StringComparison.Ordinal);
if (index != -1) if (index != -1)
{ {
rating = rating.Substring(index).TrimStart(':').Trim(); rating = rating.Substring(index).TrimStart(':').Trim();
@ -312,12 +312,12 @@ namespace Emby.Server.Implementations.Localization
throw new ArgumentNullException(nameof(culture)); throw new ArgumentNullException(nameof(culture));
} }
const string prefix = "Core"; const string Prefix = "Core";
var key = prefix + culture; var key = Prefix + culture;
return _dictionaries.GetOrAdd( return _dictionaries.GetOrAdd(
key, key,
f => GetDictionary(prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult()); f => GetDictionary(Prefix, culture, DefaultCulture + ".json").GetAwaiter().GetResult());
} }
private async Task<Dictionary<string, string>> GetDictionary(string prefix, string culture, string baseFilename) private async Task<Dictionary<string, string>> GetDictionary(string prefix, string culture, string baseFilename)

View File

@ -390,7 +390,7 @@ namespace Emby.Server.Implementations.Networking
var host = uri.DnsSafeHost; var host = uri.DnsSafeHost;
_logger.LogDebug("Resolving host {0}", host); _logger.LogDebug("Resolving host {0}", host);
address = GetIpAddresses(host).Result.FirstOrDefault(); address = GetIpAddresses(host).GetAwaiter().GetResult().FirstOrDefault();
if (address != null) if (address != null)
{ {

View File

@ -2,6 +2,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization;
using System.Linq; using System.Linq;
using System.Linq.Expressions; using System.Linq.Expressions;
using System.Reflection; using System.Reflection;
@ -105,7 +106,12 @@ namespace Emby.Server.Implementations.Services
} }
var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLowerInvariant(); var expectedMethodName = actionName.Substring(0, 1) + actionName.Substring(1).ToLowerInvariant();
throw new NotImplementedException(string.Format("Could not find method named {1}({0}) or Any({0}) on Service {2}", requestDto.GetType().GetMethodName(), expectedMethodName, serviceType.GetMethodName())); throw new NotImplementedException(
string.Format(
CultureInfo.InvariantCulture,
"Could not find method named {1}({0}) or Any({0}) on Service {2}",
requestDto.GetType().GetMethodName(),
expectedMethodName, serviceType.GetMethodName()));
} }
private static async Task<object> GetTaskResult(Task task) private static async Task<object> GetTaskResult(Task task)

View File

@ -44,7 +44,7 @@ namespace Emby.Server.Implementations.Services
var pos = pathInfo.LastIndexOf('.'); var pos = pathInfo.LastIndexOf('.');
if (pos != -1) if (pos != -1)
{ {
var format = pathInfo.Substring(pos + 1); var format = pathInfo.AsSpan().Slice(pos + 1);
contentType = GetFormatContentType(format); contentType = GetFormatContentType(format);
if (contentType != null) if (contentType != null)
{ {
@ -55,15 +55,18 @@ namespace Emby.Server.Implementations.Services
return pathInfo; return pathInfo;
} }
private static string GetFormatContentType(string format) private static string GetFormatContentType(ReadOnlySpan<char> format)
{ {
// built-in formats if (format.Equals("json", StringComparison.Ordinal))
switch (format)
{ {
case "json": return "application/json"; return "application/json";
case "xml": return "application/xml";
default: return null;
} }
else if (format.Equals("xml", StringComparison.Ordinal))
{
return "application/xml";
}
return null;
} }
public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, ILogger logger, CancellationToken cancellationToken) public async Task ProcessRequestAsync(HttpListenerHost httpHost, IRequest httpReq, HttpResponse httpRes, ILogger logger, CancellationToken cancellationToken)

View File

@ -156,7 +156,7 @@ namespace Emby.Server.Implementations.Services
{ {
var component = components[i]; var component = components[i];
if (component.StartsWith(VariablePrefix)) if (component.StartsWith(VariablePrefix, StringComparison.Ordinal))
{ {
var variableName = component.Substring(1, component.Length - 2); var variableName = component.Substring(1, component.Length - 2);
if (variableName[variableName.Length - 1] == WildCardChar) if (variableName[variableName.Length - 1] == WildCardChar)

View File

@ -369,7 +369,7 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistFilename = Path.GetFileNameWithoutExtension(playlist); var playlistFilename = Path.GetFileNameWithoutExtension(playlist);
var indexString = Path.GetFileNameWithoutExtension(file.Name).Substring(playlistFilename.Length); var indexString = Path.GetFileNameWithoutExtension(file.Name).AsSpan().Slice(playlistFilename.Length);
return int.Parse(indexString, NumberStyles.Integer, CultureInfo.InvariantCulture); return int.Parse(indexString, NumberStyles.Integer, CultureInfo.InvariantCulture);
} }

View File

@ -675,11 +675,11 @@ namespace MediaBrowser.Controller.Entities
return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture)); return System.IO.Path.Combine(basePath, "channels", ChannelId.ToString("N", CultureInfo.InvariantCulture), Id.ToString("N", CultureInfo.InvariantCulture));
} }
var idString = Id.ToString("N", CultureInfo.InvariantCulture); ReadOnlySpan<char> idString = Id.ToString("N", CultureInfo.InvariantCulture);
basePath = System.IO.Path.Combine(basePath, "library"); basePath = System.IO.Path.Combine(basePath, "library");
return System.IO.Path.Combine(basePath, idString.Substring(0, 2), idString); return System.IO.Path.Join(basePath, idString.Slice(0, 2), idString);
} }
/// <summary> /// <summary>
@ -702,26 +702,27 @@ namespace MediaBrowser.Controller.Entities
foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters) foreach (var removeChar in ConfigurationManager.Configuration.SortRemoveCharacters)
{ {
sortable = sortable.Replace(removeChar, string.Empty); sortable = sortable.Replace(removeChar, string.Empty, StringComparison.Ordinal);
} }
foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters) foreach (var replaceChar in ConfigurationManager.Configuration.SortReplaceCharacters)
{ {
sortable = sortable.Replace(replaceChar, " "); sortable = sortable.Replace(replaceChar, " ", StringComparison.Ordinal);
} }
foreach (var search in ConfigurationManager.Configuration.SortRemoveWords) foreach (var search in ConfigurationManager.Configuration.SortRemoveWords)
{ {
// Remove from beginning if a space follows // Remove from beginning if a space follows
if (sortable.StartsWith(search + " ")) if (sortable.StartsWith(search + " ", StringComparison.Ordinal))
{ {
sortable = sortable.Remove(0, search.Length + 1); sortable = sortable.Remove(0, search.Length + 1);
} }
// Remove from middle if surrounded by spaces // Remove from middle if surrounded by spaces
sortable = sortable.Replace(" " + search + " ", " "); sortable = sortable.Replace(" " + search + " ", " ", StringComparison.Ordinal);
// Remove from end if followed by a space // Remove from end if followed by a space
if (sortable.EndsWith(" " + search)) if (sortable.EndsWith(" " + search, StringComparison.Ordinal))
{ {
sortable = sortable.Remove(sortable.Length - (search.Length + 1)); sortable = sortable.Remove(sortable.Length - (search.Length + 1));
} }
@ -751,6 +752,7 @@ namespace MediaBrowser.Controller.Entities
builder.Append(chunkBuilder); builder.Append(chunkBuilder);
} }
// logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString()); // logger.LogDebug("ModifySortChunks Start: {0} End: {1}", name, builder.ToString());
return builder.ToString().RemoveDiacritics(); return builder.ToString().RemoveDiacritics();
} }

View File

@ -1588,7 +1588,7 @@ namespace MediaBrowser.Controller.MediaEncoding
{ {
outputVideoCodec ??= string.Empty; outputVideoCodec ??= string.Empty;
var outputSizeParam = string.Empty; var outputSizeParam = ReadOnlySpan<char>.Empty;
var request = state.BaseRequest; var request = state.BaseRequest;
// Add resolution params, if specified // Add resolution params, if specified
@ -1602,28 +1602,28 @@ namespace MediaBrowser.Controller.MediaEncoding
var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase); var index = outputSizeParam.IndexOf("hwdownload", StringComparison.OrdinalIgnoreCase);
if (index != -1) if (index != -1)
{ {
outputSizeParam = outputSizeParam.Substring(index); outputSizeParam = outputSizeParam.Slice(index);
} }
else else
{ {
index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase); index = outputSizeParam.IndexOf("format", StringComparison.OrdinalIgnoreCase);
if (index != -1) if (index != -1)
{ {
outputSizeParam = outputSizeParam.Substring(index); outputSizeParam = outputSizeParam.Slice(index);
} }
else else
{ {
index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase); index = outputSizeParam.IndexOf("yadif", StringComparison.OrdinalIgnoreCase);
if (index != -1) if (index != -1)
{ {
outputSizeParam = outputSizeParam.Substring(index); outputSizeParam = outputSizeParam.Slice(index);
} }
else else
{ {
index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase); index = outputSizeParam.IndexOf("scale", StringComparison.OrdinalIgnoreCase);
if (index != -1) if (index != -1)
{ {
outputSizeParam = outputSizeParam.Substring(index); outputSizeParam = outputSizeParam.Slice(index);
} }
} }
} }
@ -1669,9 +1669,9 @@ namespace MediaBrowser.Controller.MediaEncoding
// Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference) // Setup default filtergraph utilizing FFMpeg overlay() and FFMpeg scale() (see the return of this function for index reference)
// Always put the scaler before the overlay for better performance // Always put the scaler before the overlay for better performance
var retStr = !string.IsNullOrEmpty(outputSizeParam) ? var retStr = !outputSizeParam.IsEmpty
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\"" : ? " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay\""
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\""; : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay\"";
// When the input may or may not be hardware VAAPI decodable // When the input may or may not be hardware VAAPI decodable
if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) if (string.Equals(outputVideoCodec, "h264_vaapi", StringComparison.OrdinalIgnoreCase))
@ -1705,7 +1705,7 @@ namespace MediaBrowser.Controller.MediaEncoding
*/ */
if (isLinux) if (isLinux)
{ {
retStr = !string.IsNullOrEmpty(outputSizeParam) ? retStr = !outputSizeParam.IsEmpty ?
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" : " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}]{3}[base];[base][sub]overlay_qsv\"" :
" -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\""; " -filter_complex \"[{0}:{1}]{4}[sub];[0:{2}][sub]overlay_qsv\"";
} }
@ -1717,7 +1717,7 @@ namespace MediaBrowser.Controller.MediaEncoding
mapPrefix, mapPrefix,
subtitleStreamIndex, subtitleStreamIndex,
state.VideoStream.Index, state.VideoStream.Index,
outputSizeParam, outputSizeParam.ToString(),
videoSizeParam); videoSizeParam);
} }
@ -2090,7 +2090,7 @@ namespace MediaBrowser.Controller.MediaEncoding
// Add software deinterlace filter before scaling filter // Add software deinterlace filter before scaling filter
if (state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true)) if (state.DeInterlace("h264", true) || state.DeInterlace("h265", true) || state.DeInterlace("hevc", true))
{ {
var deintParam = string.Empty; string deintParam;
var inputFramerate = videoStream?.RealFrameRate; var inputFramerate = videoStream?.RealFrameRate;
// If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle // If it is already 60fps then it will create an output framerate that is much too high for roku and others to handle
@ -2159,7 +2159,6 @@ namespace MediaBrowser.Controller.MediaEncoding
return output; return output;
} }
/// <summary> /// <summary>
/// Gets the number of threads. /// Gets the number of threads.
/// </summary> /// </summary>

View File

@ -35,7 +35,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
continue; continue;
} }
if (line.StartsWith("[")) if (line[0] == '[')
{ {
break; break;
} }
@ -62,7 +62,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return trackInfo; return trackInfo;
} }
long GetTicks(string time) private long GetTicks(ReadOnlySpan<char> time)
{ {
return TimeSpan.TryParseExact(time, @"h\:mm\:ss\.ff", _usCulture, out var span) return TimeSpan.TryParseExact(time, @"h\:mm\:ss\.ff", _usCulture, out var span)
? span.Ticks : 0; ? span.Ticks : 0;

View File

@ -1,6 +1,9 @@
#nullable enable
#pragma warning disable CS1591
namespace MediaBrowser.MediaEncoding.Subtitles namespace MediaBrowser.MediaEncoding.Subtitles
{ {
public class ParserValues public static class ParserValues
{ {
public const string NewLine = "\r\n"; public const string NewLine = "\r\n";
} }

View File

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Globalization; using System.Globalization;
@ -20,6 +22,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
_logger = logger; _logger = logger;
} }
/// <inheritdoc />
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken) public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{ {
var trackInfo = new SubtitleTrackInfo(); var trackInfo = new SubtitleTrackInfo();
@ -55,11 +58,11 @@ namespace MediaBrowser.MediaEncoding.Subtitles
} }
subEvent.StartPositionTicks = GetTicks(time[0]); subEvent.StartPositionTicks = GetTicks(time[0]);
var endTime = time[1]; var endTime = time[1].AsSpan();
var idx = endTime.IndexOf(" ", StringComparison.Ordinal); var idx = endTime.IndexOf(' ');
if (idx > 0) if (idx > 0)
{ {
endTime = endTime.Substring(0, idx); endTime = endTime.Slice(0, idx);
} }
subEvent.EndPositionTicks = GetTicks(endTime); subEvent.EndPositionTicks = GetTicks(endTime);
@ -88,7 +91,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
return trackInfo; return trackInfo;
} }
long GetTicks(string time) private long GetTicks(ReadOnlySpan<char> time)
{ {
return TimeSpan.TryParseExact(time, @"hh\:mm\:ss\.fff", _usCulture, out var span) return TimeSpan.TryParseExact(time, @"hh\:mm\:ss\.fff", _usCulture, out var span)
? span.Ticks ? span.Ticks

View File

@ -8,8 +8,12 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles namespace MediaBrowser.MediaEncoding.Subtitles
{ {
/// <summary>
/// SRT subtitle writer.
/// </summary>
public class SrtWriter : ISubtitleWriter public class SrtWriter : ISubtitleWriter
{ {
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{ {
using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true)) using (var writer = new StreamWriter(stream, Encoding.UTF8, 1024, true))

View File

@ -12,6 +12,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// </summary> /// </summary>
public class SsaParser : ISubtitleParser public class SsaParser : ISubtitleParser
{ {
/// <inheritdoc />
public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken) public SubtitleTrackInfo Parse(Stream stream, CancellationToken cancellationToken)
{ {
var trackInfo = new SubtitleTrackInfo(); var trackInfo = new SubtitleTrackInfo();
@ -45,7 +46,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
header.AppendLine(line); header.AppendLine(line);
} }
if (line.Trim().ToLowerInvariant() == "[events]") if (string.Equals(line.Trim(), "[events]", StringComparison.OrdinalIgnoreCase))
{ {
eventsStarted = true; eventsStarted = true;
} }
@ -63,27 +64,27 @@ namespace MediaBrowser.MediaEncoding.Subtitles
format = line.ToLowerInvariant().Substring(8).Split(','); format = line.ToLowerInvariant().Substring(8).Split(',');
for (int i = 0; i < format.Length; i++) for (int i = 0; i < format.Length; i++)
{ {
if (format[i].Trim().ToLowerInvariant() == "layer") if (string.Equals(format[i].Trim(), "layer", StringComparison.OrdinalIgnoreCase))
{ {
indexLayer = i; indexLayer = i;
} }
else if (format[i].Trim().ToLowerInvariant() == "start") else if (string.Equals(format[i].Trim(), "start", StringComparison.OrdinalIgnoreCase))
{ {
indexStart = i; indexStart = i;
} }
else if (format[i].Trim().ToLowerInvariant() == "end") else if (string.Equals(format[i].Trim(), "end", StringComparison.OrdinalIgnoreCase))
{ {
indexEnd = i; indexEnd = i;
} }
else if (format[i].Trim().ToLowerInvariant() == "text") else if (string.Equals(format[i].Trim(), "text", StringComparison.OrdinalIgnoreCase))
{ {
indexText = i; indexText = i;
} }
else if (format[i].Trim().ToLowerInvariant() == "effect") else if (string.Equals(format[i].Trim(), "effect", StringComparison.OrdinalIgnoreCase))
{ {
indexEffect = i; indexEffect = i;
} }
else if (format[i].Trim().ToLowerInvariant() == "style") else if (string.Equals(format[i].Trim(), "style", StringComparison.OrdinalIgnoreCase))
{ {
indexStyle = i; indexStyle = i;
} }
@ -184,12 +185,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
int.Parse(timeCode[3]) * 10).Ticks; int.Parse(timeCode[3]) * 10).Ticks;
} }
public static string GetFormattedText(string text) private static string GetFormattedText(string text)
{ {
text = text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase); text = text.Replace("\\n", ParserValues.NewLine, StringComparison.OrdinalIgnoreCase);
bool italic = false;
for (int i = 0; i < 10; i++) // just look ten times... for (int i = 0; i < 10; i++) // just look ten times...
{ {
if (text.Contains(@"{\fn")) if (text.Contains(@"{\fn"))
@ -200,7 +199,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
string fontName = text.Substring(start + 4, end - (start + 4)); string fontName = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty; string extraTags = string.Empty;
CheckAndAddSubTags(ref fontName, ref extraTags, out italic); CheckAndAddSubTags(ref fontName, ref extraTags, out bool italic);
text = text.Remove(start, end - start + 1); text = text.Remove(start, end - start + 1);
if (italic) if (italic)
{ {
@ -231,7 +230,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
string fontSize = text.Substring(start + 4, end - (start + 4)); string fontSize = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty; string extraTags = string.Empty;
CheckAndAddSubTags(ref fontSize, ref extraTags, out italic); CheckAndAddSubTags(ref fontSize, ref extraTags, out bool italic);
if (IsInteger(fontSize)) if (IsInteger(fontSize))
{ {
text = text.Remove(start, end - start + 1); text = text.Remove(start, end - start + 1);
@ -265,7 +264,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
string color = text.Substring(start + 4, end - (start + 4)); string color = text.Substring(start + 4, end - (start + 4));
string extraTags = string.Empty; string extraTags = string.Empty;
CheckAndAddSubTags(ref color, ref extraTags, out italic); CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
color = color.Replace("&", string.Empty).TrimStart('H'); color = color.Replace("&", string.Empty).TrimStart('H');
color = color.PadLeft(6, '0'); color = color.PadLeft(6, '0');
@ -303,7 +302,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
{ {
string color = text.Substring(start + 5, end - (start + 5)); string color = text.Substring(start + 5, end - (start + 5));
string extraTags = string.Empty; string extraTags = string.Empty;
CheckAndAddSubTags(ref color, ref extraTags, out italic); CheckAndAddSubTags(ref color, ref extraTags, out bool italic);
color = color.Replace("&", string.Empty).TrimStart('H'); color = color.Replace("&", string.Empty).TrimStart('H');
color = color.PadLeft(6, '0'); color = color.PadLeft(6, '0');
@ -354,14 +353,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
} }
private static bool IsInteger(string s) private static bool IsInteger(string s)
{ => int.TryParse(s, out _);
if (int.TryParse(s, out var i))
{
return true;
}
return false;
}
private static int CountTagInText(string text, string tag) private static int CountTagInText(string text, string tag)
{ {

View File

@ -1,3 +1,5 @@
#pragma warning disable CS1591
using System; using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Diagnostics; using System.Diagnostics;
@ -365,7 +367,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
/// <returns>System.Object.</returns> /// <returns>System.Object.</returns>
private SemaphoreSlim GetLock(string filename) private SemaphoreSlim GetLock(string filename)
{ {
return _semaphoreLocks.GetOrAdd(filename, key => new SemaphoreSlim(1, 1)); return _semaphoreLocks.GetOrAdd(filename, _ => new SemaphoreSlim(1, 1));
} }
/// <summary> /// <summary>

View File

@ -6,8 +6,12 @@ using MediaBrowser.Model.MediaInfo;
namespace MediaBrowser.MediaEncoding.Subtitles namespace MediaBrowser.MediaEncoding.Subtitles
{ {
/// <summary>
/// TTML subtitle writer.
/// </summary>
public class TtmlWriter : ISubtitleWriter public class TtmlWriter : ISubtitleWriter
{ {
/// <inheritdoc />
public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken) public void Write(SubtitleTrackInfo info, Stream stream, CancellationToken cancellationToken)
{ {
// Example: https://github.com/zmalltalker/ttml2vtt/blob/master/data/sample.xml // Example: https://github.com/zmalltalker/ttml2vtt/blob/master/data/sample.xml
@ -36,9 +40,10 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = Regex.Replace(text, @"\\n", "<br/>", RegexOptions.IgnoreCase); text = Regex.Replace(text, @"\\n", "<br/>", RegexOptions.IgnoreCase);
writer.WriteLine("<p begin=\"{0}\" dur=\"{1}\">{2}</p>", writer.WriteLine(
"<p begin=\"{0}\" dur=\"{1}\">{2}</p>",
trackEvent.StartPositionTicks, trackEvent.StartPositionTicks,
(trackEvent.EndPositionTicks - trackEvent.StartPositionTicks), trackEvent.EndPositionTicks - trackEvent.StartPositionTicks,
text); text);
} }

View File

@ -47,7 +47,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase); text = Regex.Replace(text, @"\\n", " ", RegexOptions.IgnoreCase);
writer.WriteLine(text); writer.WriteLine(text);
writer.WriteLine(string.Empty); writer.WriteLine();
} }
} }
} }