mirror of
https://github.com/jellyfin/jellyfin.git
synced 2026-04-21 01:24:44 +01:00
Merge branch 'master' into feature/ffmpeg-version-check
This commit is contained in:
@@ -9,7 +9,7 @@ using MediaBrowser.Model.MediaInfo;
|
||||
namespace MediaBrowser.MediaEncoding.BdInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// Class BdInfoExaminer
|
||||
/// Class BdInfoExaminer.
|
||||
/// </summary>
|
||||
public class BdInfoExaminer : IBlurayExaminer
|
||||
{
|
||||
|
||||
@@ -14,23 +14,45 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
|
||||
private static readonly string[] _requiredDecoders = new[]
|
||||
{
|
||||
"h264",
|
||||
"hevc",
|
||||
"mpeg2video",
|
||||
"h264_qsv",
|
||||
"hevc_qsv",
|
||||
"mpeg2_qsv",
|
||||
"mpeg2_mmal",
|
||||
"mpeg4_mmal",
|
||||
"vc1_qsv",
|
||||
"vc1_mmal",
|
||||
"h264_cuvid",
|
||||
"hevc_cuvid",
|
||||
"mpeg4",
|
||||
"msmpeg4",
|
||||
"dts",
|
||||
"ac3",
|
||||
"aac",
|
||||
"mp3",
|
||||
"h264",
|
||||
"h264_qsv",
|
||||
"hevc_qsv",
|
||||
"mpeg2_qsv",
|
||||
"vc1_qsv",
|
||||
"vp8_qsv",
|
||||
"vp9_qsv",
|
||||
"h264_cuvid",
|
||||
"hevc_cuvid",
|
||||
"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"
|
||||
};
|
||||
|
||||
// These are the library versions that corresponds to our minimum ffmpeg version 4.x according to the version table below
|
||||
@@ -175,6 +199,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
|
||||
@@ -284,6 +310,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";
|
||||
|
||||
@@ -25,7 +25,7 @@ using System.Diagnostics;
|
||||
namespace MediaBrowser.MediaEncoding.Encoder
|
||||
{
|
||||
/// <summary>
|
||||
/// Class MediaEncoder
|
||||
/// Class MediaEncoder.
|
||||
/// </summary>
|
||||
public class MediaEncoder : IMediaEncoder, IDisposable
|
||||
{
|
||||
@@ -111,6 +111,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
|
||||
SetAvailableDecoders(validator.GetDecoders());
|
||||
SetAvailableEncoders(validator.GetEncoders());
|
||||
SetAvailableHwaccels(validator.GetHwaccels());
|
||||
}
|
||||
|
||||
_logger.LogInformation("FFmpeg: {EncoderLocation}: {FfmpegPath}", EncoderLocation, _ffmpegPath ?? string.Empty);
|
||||
@@ -225,6 +226,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
{
|
||||
return inJellyfinPath;
|
||||
}
|
||||
|
||||
var values = Environment.GetEnvironmentVariable("PATH");
|
||||
|
||||
foreach (var path in values.Split(Path.PathSeparator))
|
||||
@@ -254,6 +256,13 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
// _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)
|
||||
{
|
||||
return _encoders.Contains(encoder, StringComparer.OrdinalIgnoreCase);
|
||||
@@ -264,6 +273,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))
|
||||
@@ -422,7 +436,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The us culture
|
||||
/// The us culture.
|
||||
/// </summary>
|
||||
protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
|
||||
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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();
|
||||
@@ -515,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>
|
||||
@@ -548,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>
|
||||
@@ -767,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>
|
||||
@@ -948,6 +957,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
{
|
||||
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Composer });
|
||||
}
|
||||
|
||||
audio.People = peoples.ToArray();
|
||||
}
|
||||
|
||||
@@ -979,6 +989,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
{
|
||||
peoples.Add(new BaseItemPerson { Name = person, Type = PersonType.Writer });
|
||||
}
|
||||
|
||||
audio.People = peoples.ToArray();
|
||||
}
|
||||
|
||||
@@ -1012,6 +1023,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
{
|
||||
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album artist");
|
||||
}
|
||||
|
||||
if (string.IsNullOrWhiteSpace(albumArtist))
|
||||
{
|
||||
albumArtist = FFProbeHelpers.GetDictionaryValue(tags, "album_artist");
|
||||
@@ -1154,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>
|
||||
@@ -1175,6 +1187,7 @@ namespace MediaBrowser.MediaEncoding.Probing
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (info.AlbumArtists.Contains(studio, StringComparer.OrdinalIgnoreCase))
|
||||
{
|
||||
continue;
|
||||
@@ -1191,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>
|
||||
|
||||
@@ -23,6 +23,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
string line;
|
||||
while (reader.ReadLine() != "[Events]")
|
||||
{ }
|
||||
|
||||
var headers = ParseFieldHeaders(reader.ReadLine());
|
||||
|
||||
while ((line = reader.ReadLine()) != null)
|
||||
@@ -56,6 +57,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
trackEvents.Add(subEvent);
|
||||
}
|
||||
}
|
||||
|
||||
trackInfo.TrackEvents = trackEvents.ToArray();
|
||||
return trackInfo;
|
||||
}
|
||||
@@ -112,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
|
||||
// subtitle.Renumber(1);
|
||||
}
|
||||
|
||||
trackInfo.TrackEvents = trackEvents.ToArray();
|
||||
return trackInfo;
|
||||
}
|
||||
@@ -302,6 +303,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
return count;
|
||||
index = text.IndexOf(tag, index + 1);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
@@ -329,6 +331,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
rest = string.Empty;
|
||||
}
|
||||
|
||||
extraTags += " size=\"" + fontSize.Substring(2) + "\"";
|
||||
}
|
||||
else if (rest.StartsWith("fn") && rest.Length > 2)
|
||||
@@ -344,6 +347,7 @@ namespace MediaBrowser.MediaEncoding.Subtitles
|
||||
{
|
||||
rest = string.Empty;
|
||||
}
|
||||
|
||||
extraTags += " face=\"" + fontName.Substring(2) + "\"";
|
||||
}
|
||||
else if (rest.StartsWith("c") && rest.Length > 2)
|
||||
|
||||
@@ -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>();
|
||||
@@ -736,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)))
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user