Merge pull request #844 from ploughpuff/ffmpeg
Reworked FFmpeg path discovery and always display to user
This commit is contained in:
commit
a4b52b7264
|
@ -23,6 +23,7 @@
|
||||||
- [fruhnow](https://github.com/fruhnow)
|
- [fruhnow](https://github.com/fruhnow)
|
||||||
- [Lynxy](https://github.com/Lynxy)
|
- [Lynxy](https://github.com/Lynxy)
|
||||||
- [fasheng](https://github.com/fasheng)
|
- [fasheng](https://github.com/fasheng)
|
||||||
|
- [ploughpuff](https://github.com/ploughpuff)
|
||||||
|
|
||||||
# Emby Contributors
|
# Emby Contributors
|
||||||
|
|
||||||
|
|
|
@ -28,7 +28,6 @@ using Emby.Server.Implementations.Data;
|
||||||
using Emby.Server.Implementations.Devices;
|
using Emby.Server.Implementations.Devices;
|
||||||
using Emby.Server.Implementations.Diagnostics;
|
using Emby.Server.Implementations.Diagnostics;
|
||||||
using Emby.Server.Implementations.Dto;
|
using Emby.Server.Implementations.Dto;
|
||||||
using Emby.Server.Implementations.FFMpeg;
|
|
||||||
using Emby.Server.Implementations.HttpServer;
|
using Emby.Server.Implementations.HttpServer;
|
||||||
using Emby.Server.Implementations.HttpServer.Security;
|
using Emby.Server.Implementations.HttpServer.Security;
|
||||||
using Emby.Server.Implementations.IO;
|
using Emby.Server.Implementations.IO;
|
||||||
|
@ -535,7 +534,7 @@ namespace Emby.Server.Implementations
|
||||||
|
|
||||||
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
|
ConfigurationManager.ConfigurationUpdated += OnConfigurationUpdated;
|
||||||
|
|
||||||
MediaEncoder.Init();
|
MediaEncoder.SetFFmpegPath();
|
||||||
|
|
||||||
//if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath))
|
//if (string.IsNullOrWhiteSpace(MediaEncoder.EncoderPath))
|
||||||
//{
|
//{
|
||||||
|
@ -790,7 +789,18 @@ namespace Emby.Server.Implementations
|
||||||
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
|
ChapterManager = new ChapterManager(LibraryManager, LoggerFactory, ServerConfigurationManager, ItemRepository);
|
||||||
serviceCollection.AddSingleton(ChapterManager);
|
serviceCollection.AddSingleton(ChapterManager);
|
||||||
|
|
||||||
RegisterMediaEncoder(serviceCollection);
|
MediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
|
||||||
|
LoggerFactory,
|
||||||
|
JsonSerializer,
|
||||||
|
StartupOptions.FFmpegPath,
|
||||||
|
StartupOptions.FFprobePath,
|
||||||
|
ServerConfigurationManager,
|
||||||
|
FileSystemManager,
|
||||||
|
() => SubtitleEncoder,
|
||||||
|
() => MediaSourceManager,
|
||||||
|
ProcessFactory,
|
||||||
|
5000);
|
||||||
|
serviceCollection.AddSingleton(MediaEncoder);
|
||||||
|
|
||||||
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
|
EncodingManager = new MediaEncoder.EncodingManager(FileSystemManager, LoggerFactory, MediaEncoder, ChapterManager, LibraryManager);
|
||||||
serviceCollection.AddSingleton(EncodingManager);
|
serviceCollection.AddSingleton(EncodingManager);
|
||||||
|
@ -906,85 +916,6 @@ namespace Emby.Server.Implementations
|
||||||
return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
|
return new ImageProcessor(LoggerFactory, ServerConfigurationManager.ApplicationPaths, FileSystemManager, ImageEncoder, () => LibraryManager, () => MediaEncoder);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected virtual FFMpegInstallInfo GetFfmpegInstallInfo()
|
|
||||||
{
|
|
||||||
var info = new FFMpegInstallInfo();
|
|
||||||
|
|
||||||
// Windows builds: http://ffmpeg.zeranoe.com/builds/
|
|
||||||
// Linux builds: http://johnvansickle.com/ffmpeg/
|
|
||||||
// OS X builds: http://ffmpegmac.net/
|
|
||||||
// OS X x64: http://www.evermeet.cx/ffmpeg/
|
|
||||||
|
|
||||||
if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Linux)
|
|
||||||
{
|
|
||||||
info.FFMpegFilename = "ffmpeg";
|
|
||||||
info.FFProbeFilename = "ffprobe";
|
|
||||||
info.ArchiveType = "7z";
|
|
||||||
info.Version = "20170308";
|
|
||||||
}
|
|
||||||
else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows)
|
|
||||||
{
|
|
||||||
info.FFMpegFilename = "ffmpeg.exe";
|
|
||||||
info.FFProbeFilename = "ffprobe.exe";
|
|
||||||
info.Version = "20170308";
|
|
||||||
info.ArchiveType = "7z";
|
|
||||||
}
|
|
||||||
else if (EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.OSX)
|
|
||||||
{
|
|
||||||
info.FFMpegFilename = "ffmpeg";
|
|
||||||
info.FFProbeFilename = "ffprobe";
|
|
||||||
info.ArchiveType = "7z";
|
|
||||||
info.Version = "20170308";
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected virtual FFMpegInfo GetFFMpegInfo()
|
|
||||||
{
|
|
||||||
return new FFMpegLoader(ApplicationPaths, FileSystemManager, GetFfmpegInstallInfo())
|
|
||||||
.GetFFMpegInfo(StartupOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registers the media encoder.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>Task.</returns>
|
|
||||||
private void RegisterMediaEncoder(IServiceCollection serviceCollection)
|
|
||||||
{
|
|
||||||
string encoderPath = null;
|
|
||||||
string probePath = null;
|
|
||||||
|
|
||||||
var info = GetFFMpegInfo();
|
|
||||||
|
|
||||||
encoderPath = info.EncoderPath;
|
|
||||||
probePath = info.ProbePath;
|
|
||||||
var hasExternalEncoder = string.Equals(info.Version, "external", StringComparison.OrdinalIgnoreCase);
|
|
||||||
|
|
||||||
var mediaEncoder = new MediaBrowser.MediaEncoding.Encoder.MediaEncoder(
|
|
||||||
LoggerFactory,
|
|
||||||
JsonSerializer,
|
|
||||||
encoderPath,
|
|
||||||
probePath,
|
|
||||||
hasExternalEncoder,
|
|
||||||
ServerConfigurationManager,
|
|
||||||
FileSystemManager,
|
|
||||||
LiveTvManager,
|
|
||||||
IsoManager,
|
|
||||||
LibraryManager,
|
|
||||||
ChannelManager,
|
|
||||||
SessionManager,
|
|
||||||
() => SubtitleEncoder,
|
|
||||||
() => MediaSourceManager,
|
|
||||||
HttpClient,
|
|
||||||
ZipClient,
|
|
||||||
ProcessFactory,
|
|
||||||
5000);
|
|
||||||
|
|
||||||
MediaEncoder = mediaEncoder;
|
|
||||||
serviceCollection.AddSingleton(MediaEncoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the user repository.
|
/// Gets the user repository.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -1460,7 +1391,7 @@ namespace Emby.Server.Implementations
|
||||||
ServerName = FriendlyName,
|
ServerName = FriendlyName,
|
||||||
LocalAddress = localAddress,
|
LocalAddress = localAddress,
|
||||||
SupportsLibraryMonitor = true,
|
SupportsLibraryMonitor = true,
|
||||||
EncoderLocationType = MediaEncoder.EncoderLocationType,
|
EncoderLocation = MediaEncoder.EncoderLocation,
|
||||||
SystemArchitecture = EnvironmentInfo.SystemArchitecture,
|
SystemArchitecture = EnvironmentInfo.SystemArchitecture,
|
||||||
SystemUpdateLevel = SystemUpdateLevel,
|
SystemUpdateLevel = SystemUpdateLevel,
|
||||||
PackageName = StartupOptions.PackageName
|
PackageName = StartupOptions.PackageName
|
||||||
|
|
|
@ -1,24 +0,0 @@
|
||||||
namespace Emby.Server.Implementations.FFMpeg
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Class FFMpegInfo
|
|
||||||
/// </summary>
|
|
||||||
public class FFMpegInfo
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the path.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The path.</value>
|
|
||||||
public string EncoderPath { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the probe path.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The probe path.</value>
|
|
||||||
public string ProbePath { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the version.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The version.</value>
|
|
||||||
public string Version { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,17 +0,0 @@
|
||||||
namespace Emby.Server.Implementations.FFMpeg
|
|
||||||
{
|
|
||||||
public class FFMpegInstallInfo
|
|
||||||
{
|
|
||||||
public string Version { get; set; }
|
|
||||||
public string FFMpegFilename { get; set; }
|
|
||||||
public string FFProbeFilename { get; set; }
|
|
||||||
public string ArchiveType { get; set; }
|
|
||||||
|
|
||||||
public FFMpegInstallInfo()
|
|
||||||
{
|
|
||||||
Version = "Path";
|
|
||||||
FFMpegFilename = "ffmpeg";
|
|
||||||
FFProbeFilename = "ffprobe";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,132 +0,0 @@
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using MediaBrowser.Common.Configuration;
|
|
||||||
using MediaBrowser.Model.IO;
|
|
||||||
|
|
||||||
namespace Emby.Server.Implementations.FFMpeg
|
|
||||||
{
|
|
||||||
public class FFMpegLoader
|
|
||||||
{
|
|
||||||
private readonly IApplicationPaths _appPaths;
|
|
||||||
private readonly IFileSystem _fileSystem;
|
|
||||||
private readonly FFMpegInstallInfo _ffmpegInstallInfo;
|
|
||||||
|
|
||||||
public FFMpegLoader(IApplicationPaths appPaths, IFileSystem fileSystem, FFMpegInstallInfo ffmpegInstallInfo)
|
|
||||||
{
|
|
||||||
_appPaths = appPaths;
|
|
||||||
_fileSystem = fileSystem;
|
|
||||||
_ffmpegInstallInfo = ffmpegInstallInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
public FFMpegInfo GetFFMpegInfo(IStartupOptions options)
|
|
||||||
{
|
|
||||||
var customffMpegPath = options.FFmpegPath;
|
|
||||||
var customffProbePath = options.FFprobePath;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(customffMpegPath) && !string.IsNullOrWhiteSpace(customffProbePath))
|
|
||||||
{
|
|
||||||
return new FFMpegInfo
|
|
||||||
{
|
|
||||||
ProbePath = customffProbePath,
|
|
||||||
EncoderPath = customffMpegPath,
|
|
||||||
Version = "external"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var downloadInfo = _ffmpegInstallInfo;
|
|
||||||
|
|
||||||
var prebuiltFolder = _appPaths.ProgramSystemPath;
|
|
||||||
var prebuiltffmpeg = Path.Combine(prebuiltFolder, downloadInfo.FFMpegFilename);
|
|
||||||
var prebuiltffprobe = Path.Combine(prebuiltFolder, downloadInfo.FFProbeFilename);
|
|
||||||
if (File.Exists(prebuiltffmpeg) && File.Exists(prebuiltffprobe))
|
|
||||||
{
|
|
||||||
return new FFMpegInfo
|
|
||||||
{
|
|
||||||
ProbePath = prebuiltffprobe,
|
|
||||||
EncoderPath = prebuiltffmpeg,
|
|
||||||
Version = "external"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
var version = downloadInfo.Version;
|
|
||||||
|
|
||||||
if (string.Equals(version, "0", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
return new FFMpegInfo();
|
|
||||||
}
|
|
||||||
|
|
||||||
var rootEncoderPath = Path.Combine(_appPaths.ProgramDataPath, "ffmpeg");
|
|
||||||
var versionedDirectoryPath = Path.Combine(rootEncoderPath, version);
|
|
||||||
|
|
||||||
var info = new FFMpegInfo
|
|
||||||
{
|
|
||||||
ProbePath = Path.Combine(versionedDirectoryPath, downloadInfo.FFProbeFilename),
|
|
||||||
EncoderPath = Path.Combine(versionedDirectoryPath, downloadInfo.FFMpegFilename),
|
|
||||||
Version = version
|
|
||||||
};
|
|
||||||
|
|
||||||
Directory.CreateDirectory(versionedDirectoryPath);
|
|
||||||
|
|
||||||
var excludeFromDeletions = new List<string> { versionedDirectoryPath };
|
|
||||||
|
|
||||||
if (!File.Exists(info.ProbePath) || !File.Exists(info.EncoderPath))
|
|
||||||
{
|
|
||||||
// ffmpeg not present. See if there's an older version we can start with
|
|
||||||
var existingVersion = GetExistingVersion(info, rootEncoderPath);
|
|
||||||
|
|
||||||
// No older version. Need to download and block until complete
|
|
||||||
if (existingVersion == null)
|
|
||||||
{
|
|
||||||
return new FFMpegInfo();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
info = existingVersion;
|
|
||||||
versionedDirectoryPath = Path.GetDirectoryName(info.EncoderPath);
|
|
||||||
excludeFromDeletions.Add(versionedDirectoryPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Allow just one of these to be overridden, if desired.
|
|
||||||
if (!string.IsNullOrWhiteSpace(customffMpegPath))
|
|
||||||
{
|
|
||||||
info.EncoderPath = customffMpegPath;
|
|
||||||
}
|
|
||||||
if (!string.IsNullOrWhiteSpace(customffProbePath))
|
|
||||||
{
|
|
||||||
info.ProbePath = customffProbePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
return info;
|
|
||||||
}
|
|
||||||
|
|
||||||
private FFMpegInfo GetExistingVersion(FFMpegInfo info, string rootEncoderPath)
|
|
||||||
{
|
|
||||||
var encoderFilename = Path.GetFileName(info.EncoderPath);
|
|
||||||
var probeFilename = Path.GetFileName(info.ProbePath);
|
|
||||||
|
|
||||||
foreach (var directory in _fileSystem.GetDirectoryPaths(rootEncoderPath))
|
|
||||||
{
|
|
||||||
var allFiles = _fileSystem.GetFilePaths(directory, true).ToList();
|
|
||||||
|
|
||||||
var encoder = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), encoderFilename, StringComparison.OrdinalIgnoreCase));
|
|
||||||
var probe = allFiles.FirstOrDefault(i => string.Equals(Path.GetFileName(i), probeFilename, StringComparison.OrdinalIgnoreCase));
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(encoder) &&
|
|
||||||
!string.IsNullOrWhiteSpace(probe))
|
|
||||||
{
|
|
||||||
return new FFMpegInfo
|
|
||||||
{
|
|
||||||
EncoderPath = encoder,
|
|
||||||
ProbePath = probe,
|
|
||||||
Version = Path.GetFileName(Path.GetDirectoryName(probe))
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -20,10 +20,10 @@ namespace Jellyfin.Server
|
||||||
[Option('l', "logdir", Required = false, HelpText = "Path to use for writing log files.")]
|
[Option('l', "logdir", Required = false, HelpText = "Path to use for writing log files.")]
|
||||||
public string LogDir { get; set; }
|
public string LogDir { get; set; }
|
||||||
|
|
||||||
[Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH. Must be specified along with --ffprobe.")]
|
[Option("ffmpeg", Required = false, HelpText = "Path to external FFmpeg executable to use in place of default found in PATH.")]
|
||||||
public string FFmpegPath { get; set; }
|
public string FFmpegPath { get; set; }
|
||||||
|
|
||||||
[Option("ffprobe", Required = false, HelpText = "Path to external FFprobe executable to use in place of default found in PATH. Must be specified along with --ffmpeg.")]
|
[Option("ffprobe", Required = false, HelpText = "(deprecated) Option has no effect and shall be removed in next release.")]
|
||||||
public string FFprobePath { get; set; }
|
public string FFprobePath { get; set; }
|
||||||
|
|
||||||
[Option("service", Required = false, HelpText = "Run as headless service.")]
|
[Option("service", Required = false, HelpText = "Run as headless service.")]
|
||||||
|
|
|
@ -6,6 +6,7 @@ using MediaBrowser.Model.Dlna;
|
||||||
using MediaBrowser.Model.Entities;
|
using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
|
using MediaBrowser.Model.System;
|
||||||
|
|
||||||
namespace MediaBrowser.Controller.MediaEncoding
|
namespace MediaBrowser.Controller.MediaEncoding
|
||||||
{
|
{
|
||||||
|
@ -14,7 +15,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IMediaEncoder : ITranscoderSupport
|
public interface IMediaEncoder : ITranscoderSupport
|
||||||
{
|
{
|
||||||
string EncoderLocationType { get; }
|
FFmpegLocation EncoderLocation { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the encoder path.
|
/// Gets the encoder path.
|
||||||
|
@ -91,7 +92,7 @@ namespace MediaBrowser.Controller.MediaEncoding
|
||||||
/// <returns>System.String.</returns>
|
/// <returns>System.String.</returns>
|
||||||
string EscapeSubtitleFilterPath(string path);
|
string EscapeSubtitleFilterPath(string path);
|
||||||
|
|
||||||
void Init();
|
void SetFFmpegPath();
|
||||||
|
|
||||||
void UpdateEncoderPath(string path, string pathType);
|
void UpdateEncoderPath(string path, string pathType);
|
||||||
bool SupportsEncoder(string encoder);
|
bool SupportsEncoder(string encoder);
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
_processFactory = processFactory;
|
_processFactory = processFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
public (IEnumerable<string> decoders, IEnumerable<string> encoders) Validate(string encoderPath)
|
public (IEnumerable<string> decoders, IEnumerable<string> encoders) GetAvailableCoders(string encoderPath)
|
||||||
{
|
{
|
||||||
_logger.LogInformation("Validating media encoder at {EncoderPath}", encoderPath);
|
_logger.LogInformation("Validating media encoder at {EncoderPath}", encoderPath);
|
||||||
|
|
||||||
|
@ -48,6 +48,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(output))
|
if (string.IsNullOrWhiteSpace(output))
|
||||||
{
|
{
|
||||||
|
if (logOutput)
|
||||||
|
{
|
||||||
|
_logger.LogError("FFmpeg validation: The process returned no result");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -55,6 +59,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1)
|
if (output.IndexOf("Libav developers", StringComparison.OrdinalIgnoreCase) != -1)
|
||||||
{
|
{
|
||||||
|
if (logOutput)
|
||||||
|
{
|
||||||
|
_logger.LogError("FFmpeg validation: avconv instead of ffmpeg is not supported");
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,17 +3,14 @@ using System.Collections.Generic;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using MediaBrowser.Common.Configuration;
|
using MediaBrowser.Common.Configuration;
|
||||||
using MediaBrowser.Common.Extensions;
|
using MediaBrowser.Common.Extensions;
|
||||||
using MediaBrowser.Common.Net;
|
|
||||||
using MediaBrowser.Controller.Channels;
|
|
||||||
using MediaBrowser.Controller.Configuration;
|
using MediaBrowser.Controller.Configuration;
|
||||||
using MediaBrowser.Controller.Library;
|
using MediaBrowser.Controller.Library;
|
||||||
using MediaBrowser.Controller.LiveTv;
|
|
||||||
using MediaBrowser.Controller.MediaEncoding;
|
using MediaBrowser.Controller.MediaEncoding;
|
||||||
using MediaBrowser.Controller.Session;
|
|
||||||
using MediaBrowser.MediaEncoding.Probing;
|
using MediaBrowser.MediaEncoding.Probing;
|
||||||
using MediaBrowser.Model.Configuration;
|
using MediaBrowser.Model.Configuration;
|
||||||
using MediaBrowser.Model.Diagnostics;
|
using MediaBrowser.Model.Diagnostics;
|
||||||
|
@ -22,6 +19,7 @@ using MediaBrowser.Model.Entities;
|
||||||
using MediaBrowser.Model.IO;
|
using MediaBrowser.Model.IO;
|
||||||
using MediaBrowser.Model.MediaInfo;
|
using MediaBrowser.Model.MediaInfo;
|
||||||
using MediaBrowser.Model.Serialization;
|
using MediaBrowser.Model.Serialization;
|
||||||
|
using MediaBrowser.Model.System;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
|
||||||
namespace MediaBrowser.MediaEncoding.Encoder
|
namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
@ -32,340 +30,223 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
public class MediaEncoder : IMediaEncoder, IDisposable
|
public class MediaEncoder : IMediaEncoder, IDisposable
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The _logger
|
/// Gets the encoder path.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
/// <value>The encoder path.</value>
|
||||||
|
public string EncoderPath => FFmpegPath;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The location of the discovered FFmpeg tool.
|
||||||
|
/// </summary>
|
||||||
|
public FFmpegLocation EncoderLocation { get; private set; }
|
||||||
|
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the json serializer.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The json serializer.</value>
|
|
||||||
private readonly IJsonSerializer _jsonSerializer;
|
private readonly IJsonSerializer _jsonSerializer;
|
||||||
|
private string FFmpegPath;
|
||||||
/// <summary>
|
private string FFprobePath;
|
||||||
/// The _thumbnail resource pool
|
|
||||||
/// </summary>
|
|
||||||
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
|
|
||||||
|
|
||||||
public string FFMpegPath { get; private set; }
|
|
||||||
|
|
||||||
public string FFProbePath { get; private set; }
|
|
||||||
|
|
||||||
protected readonly IServerConfigurationManager ConfigurationManager;
|
protected readonly IServerConfigurationManager ConfigurationManager;
|
||||||
protected readonly IFileSystem FileSystem;
|
protected readonly IFileSystem FileSystem;
|
||||||
protected readonly ILiveTvManager LiveTvManager;
|
|
||||||
protected readonly IIsoManager IsoManager;
|
|
||||||
protected readonly ILibraryManager LibraryManager;
|
|
||||||
protected readonly IChannelManager ChannelManager;
|
|
||||||
protected readonly ISessionManager SessionManager;
|
|
||||||
protected readonly Func<ISubtitleEncoder> SubtitleEncoder;
|
protected readonly Func<ISubtitleEncoder> SubtitleEncoder;
|
||||||
protected readonly Func<IMediaSourceManager> MediaSourceManager;
|
protected readonly Func<IMediaSourceManager> MediaSourceManager;
|
||||||
private readonly IHttpClient _httpClient;
|
|
||||||
private readonly IZipClient _zipClient;
|
|
||||||
private readonly IProcessFactory _processFactory;
|
private readonly IProcessFactory _processFactory;
|
||||||
|
|
||||||
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
|
||||||
private readonly bool _hasExternalEncoder;
|
|
||||||
private readonly string _originalFFMpegPath;
|
|
||||||
private readonly string _originalFFProbePath;
|
|
||||||
private readonly int DefaultImageExtractionTimeoutMs;
|
private readonly int DefaultImageExtractionTimeoutMs;
|
||||||
|
private readonly string StartupOptionFFmpegPath;
|
||||||
|
private readonly string StartupOptionFFprobePath;
|
||||||
|
|
||||||
|
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
|
||||||
|
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
||||||
|
|
||||||
public MediaEncoder(
|
public MediaEncoder(
|
||||||
ILoggerFactory loggerFactory,
|
ILoggerFactory loggerFactory,
|
||||||
IJsonSerializer jsonSerializer,
|
IJsonSerializer jsonSerializer,
|
||||||
string ffMpegPath,
|
string startupOptionsFFmpegPath,
|
||||||
string ffProbePath,
|
string startupOptionsFFprobePath,
|
||||||
bool hasExternalEncoder,
|
|
||||||
IServerConfigurationManager configurationManager,
|
IServerConfigurationManager configurationManager,
|
||||||
IFileSystem fileSystem,
|
IFileSystem fileSystem,
|
||||||
ILiveTvManager liveTvManager,
|
|
||||||
IIsoManager isoManager,
|
|
||||||
ILibraryManager libraryManager,
|
|
||||||
IChannelManager channelManager,
|
|
||||||
ISessionManager sessionManager,
|
|
||||||
Func<ISubtitleEncoder> subtitleEncoder,
|
Func<ISubtitleEncoder> subtitleEncoder,
|
||||||
Func<IMediaSourceManager> mediaSourceManager,
|
Func<IMediaSourceManager> mediaSourceManager,
|
||||||
IHttpClient httpClient,
|
|
||||||
IZipClient zipClient,
|
|
||||||
IProcessFactory processFactory,
|
IProcessFactory processFactory,
|
||||||
int defaultImageExtractionTimeoutMs)
|
int defaultImageExtractionTimeoutMs)
|
||||||
{
|
{
|
||||||
_logger = loggerFactory.CreateLogger(nameof(MediaEncoder));
|
_logger = loggerFactory.CreateLogger(nameof(MediaEncoder));
|
||||||
_jsonSerializer = jsonSerializer;
|
_jsonSerializer = jsonSerializer;
|
||||||
|
StartupOptionFFmpegPath = startupOptionsFFmpegPath;
|
||||||
|
StartupOptionFFprobePath = startupOptionsFFprobePath;
|
||||||
ConfigurationManager = configurationManager;
|
ConfigurationManager = configurationManager;
|
||||||
FileSystem = fileSystem;
|
FileSystem = fileSystem;
|
||||||
LiveTvManager = liveTvManager;
|
|
||||||
IsoManager = isoManager;
|
|
||||||
LibraryManager = libraryManager;
|
|
||||||
ChannelManager = channelManager;
|
|
||||||
SessionManager = sessionManager;
|
|
||||||
SubtitleEncoder = subtitleEncoder;
|
SubtitleEncoder = subtitleEncoder;
|
||||||
MediaSourceManager = mediaSourceManager;
|
|
||||||
_httpClient = httpClient;
|
|
||||||
_zipClient = zipClient;
|
|
||||||
_processFactory = processFactory;
|
_processFactory = processFactory;
|
||||||
DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
|
DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
|
||||||
FFProbePath = ffProbePath;
|
|
||||||
FFMpegPath = ffMpegPath;
|
|
||||||
_originalFFProbePath = ffProbePath;
|
|
||||||
_originalFFMpegPath = ffMpegPath;
|
|
||||||
_hasExternalEncoder = hasExternalEncoder;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string EncoderLocationType
|
/// <summary>
|
||||||
|
/// Run at startup or if the user removes a Custom path from transcode page.
|
||||||
|
/// Sets global variables FFmpegPath.
|
||||||
|
/// Precedence is: Config > CLI > $PATH
|
||||||
|
/// </summary>
|
||||||
|
public void SetFFmpegPath()
|
||||||
{
|
{
|
||||||
get
|
// ToDo - Finalise removal of the --ffprobe switch
|
||||||
|
if (!string.IsNullOrEmpty(StartupOptionFFprobePath))
|
||||||
{
|
{
|
||||||
if (_hasExternalEncoder)
|
_logger.LogWarning("--ffprobe switch is deprecated and shall be removed in the next release");
|
||||||
{
|
|
||||||
return "External";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(FFMpegPath))
|
// 1) Custom path stored in config/encoding xml file under tag <EncoderAppPath> takes precedence
|
||||||
|
if (!ValidatePath(ConfigurationManager.GetConfiguration<EncodingOptions>("encoding").EncoderAppPath, FFmpegLocation.Custom))
|
||||||
{
|
{
|
||||||
return null;
|
// 2) Check if the --ffmpeg CLI switch has been given
|
||||||
}
|
if (!ValidatePath(StartupOptionFFmpegPath, FFmpegLocation.SetByArgument))
|
||||||
|
|
||||||
if (IsSystemInstalledPath(FFMpegPath))
|
|
||||||
{
|
{
|
||||||
return "System";
|
// 3) Search system $PATH environment variable for valid FFmpeg
|
||||||
|
if (!ValidatePath(ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System))
|
||||||
|
{
|
||||||
|
EncoderLocation = FFmpegLocation.NotFound;
|
||||||
|
FFmpegPath = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return "Custom";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool IsSystemInstalledPath(string path)
|
// Write the FFmpeg path to the config/encoding.xml file as <EncoderAppPathDisplay> so it appears in UI
|
||||||
{
|
var config = ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
||||||
if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1)
|
config.EncoderAppPathDisplay = FFmpegPath ?? string.Empty;
|
||||||
{
|
ConfigurationManager.SaveConfiguration("encoding", config);
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
// Only if mpeg path is set, try and set path to probe
|
||||||
}
|
if (FFmpegPath != null)
|
||||||
|
|
||||||
public void Init()
|
|
||||||
{
|
{
|
||||||
InitPaths();
|
// Determine a probe path from the mpeg path
|
||||||
|
FFprobePath = Regex.Replace(FFmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1");
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(FFMpegPath))
|
// Interrogate to understand what coders are supported
|
||||||
{
|
var result = new EncoderValidator(_logger, _processFactory).GetAvailableCoders(FFmpegPath);
|
||||||
var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
|
|
||||||
|
|
||||||
SetAvailableDecoders(result.decoders);
|
SetAvailableDecoders(result.decoders);
|
||||||
SetAvailableEncoders(result.encoders);
|
SetAvailableEncoders(result.encoders);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation.ToString(), FFmpegPath ?? string.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InitPaths()
|
/// <summary>
|
||||||
{
|
/// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use.
|
||||||
ConfigureEncoderPaths();
|
/// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here.
|
||||||
|
/// </summary>
|
||||||
if (_hasExternalEncoder)
|
/// <param name="path"></param>
|
||||||
{
|
/// <param name="pathType"></param>
|
||||||
LogPaths();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the path was passed in, save it into config now.
|
|
||||||
var encodingOptions = GetEncodingOptions();
|
|
||||||
var appPath = encodingOptions.EncoderAppPath;
|
|
||||||
|
|
||||||
var valueToSave = FFMpegPath;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(valueToSave))
|
|
||||||
{
|
|
||||||
// if using system variable, don't save this.
|
|
||||||
if (IsSystemInstalledPath(valueToSave) || _hasExternalEncoder)
|
|
||||||
{
|
|
||||||
valueToSave = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.Equals(valueToSave, appPath, StringComparison.Ordinal))
|
|
||||||
{
|
|
||||||
encodingOptions.EncoderAppPath = valueToSave;
|
|
||||||
ConfigurationManager.SaveConfiguration("encoding", encodingOptions);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateEncoderPath(string path, string pathType)
|
public void UpdateEncoderPath(string path, string pathType)
|
||||||
{
|
{
|
||||||
if (_hasExternalEncoder)
|
string newPath;
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_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 {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty);
|
||||||
|
|
||||||
Tuple<string, string> newPaths;
|
if (!string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
|
||||||
|
|
||||||
if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
path = "ffmpeg";
|
|
||||||
|
|
||||||
newPaths = TestForInstalledVersions();
|
|
||||||
}
|
|
||||||
else if (string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(path))
|
|
||||||
{
|
|
||||||
throw new ArgumentNullException(nameof(path));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!File.Exists(path) && !Directory.Exists(path))
|
|
||||||
{
|
|
||||||
throw new ResourceNotFoundException();
|
|
||||||
}
|
|
||||||
newPaths = GetEncoderPaths(path);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
throw new ArgumentException("Unexpected pathType value");
|
throw new ArgumentException("Unexpected pathType value");
|
||||||
}
|
}
|
||||||
|
else if (string.IsNullOrWhiteSpace(path))
|
||||||
if (string.IsNullOrWhiteSpace(newPaths.Item1))
|
|
||||||
{
|
{
|
||||||
throw new ResourceNotFoundException("ffmpeg not found");
|
// User had cleared the custom path in UI
|
||||||
|
newPath = string.Empty;
|
||||||
}
|
}
|
||||||
if (string.IsNullOrWhiteSpace(newPaths.Item2))
|
else if (File.Exists(path))
|
||||||
{
|
{
|
||||||
throw new ResourceNotFoundException("ffprobe not found");
|
newPath = path;
|
||||||
|
}
|
||||||
|
else if (Directory.Exists(path))
|
||||||
|
{
|
||||||
|
// Given path is directory, so resolve down to filename
|
||||||
|
newPath = GetEncoderPathFromDirectory(path, "ffmpeg");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
throw new ResourceNotFoundException();
|
||||||
}
|
}
|
||||||
|
|
||||||
path = newPaths.Item1;
|
// Write the new ffmpeg path to the xml as <EncoderAppPath>
|
||||||
|
// This ensures its not lost on next startup
|
||||||
if (!ValidateVersion(path, true))
|
var config = ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
||||||
{
|
config.EncoderAppPath = newPath;
|
||||||
throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var config = GetEncodingOptions();
|
|
||||||
config.EncoderAppPath = path;
|
|
||||||
ConfigurationManager.SaveConfiguration("encoding", config);
|
ConfigurationManager.SaveConfiguration("encoding", config);
|
||||||
|
|
||||||
Init();
|
// Trigger SetFFmpegPath so we validate the new path and setup probe path
|
||||||
|
SetFFmpegPath();
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ValidateVersion(string path, bool logOutput)
|
/// <summary>
|
||||||
|
/// Validates the supplied FQPN to ensure it is a ffmpeg utility.
|
||||||
|
/// If checks pass, global variable FFmpegPath and EncoderLocation are updated.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">FQPN to test</param>
|
||||||
|
/// <param name="location">Location (External, Custom, System) of tool</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private bool ValidatePath(string path, FFmpegLocation location)
|
||||||
{
|
{
|
||||||
return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
|
bool rc = false;
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(path))
|
||||||
|
{
|
||||||
|
if (File.Exists(path))
|
||||||
|
{
|
||||||
|
rc = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, true);
|
||||||
|
|
||||||
|
if (!rc)
|
||||||
|
{
|
||||||
|
_logger.LogWarning("FFmpeg: {0}: Failed version check: {1}", location.ToString(), path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConfigureEncoderPaths()
|
// ToDo - Enable the ffmpeg validator. At the moment any version can be used.
|
||||||
{
|
rc = true;
|
||||||
if (_hasExternalEncoder)
|
|
||||||
{
|
FFmpegPath = path;
|
||||||
return;
|
EncoderLocation = location;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
var appPath = GetEncodingOptions().EncoderAppPath;
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(appPath))
|
|
||||||
{
|
{
|
||||||
appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg");
|
_logger.LogWarning("FFmpeg: {0}: File not found: {1}", location.ToString(), path);
|
||||||
}
|
|
||||||
|
|
||||||
var newPaths = GetEncoderPaths(appPath);
|
|
||||||
if (string.IsNullOrWhiteSpace(newPaths.Item1) || string.IsNullOrWhiteSpace(newPaths.Item2) || IsSystemInstalledPath(appPath))
|
|
||||||
{
|
|
||||||
newPaths = TestForInstalledVersions();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(newPaths.Item1) && !string.IsNullOrWhiteSpace(newPaths.Item2))
|
|
||||||
{
|
|
||||||
FFMpegPath = newPaths.Item1;
|
|
||||||
FFProbePath = newPaths.Item2;
|
|
||||||
}
|
|
||||||
|
|
||||||
LogPaths();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tuple<string, string> GetEncoderPaths(string configuredPath)
|
|
||||||
{
|
|
||||||
var appPath = configuredPath;
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(appPath))
|
|
||||||
{
|
|
||||||
if (Directory.Exists(appPath))
|
|
||||||
{
|
|
||||||
return GetPathsFromDirectory(appPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (File.Exists(appPath))
|
|
||||||
{
|
|
||||||
return new Tuple<string, string>(appPath, GetProbePathFromEncoderPath(appPath));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Tuple<string, string>(null, null);
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Tuple<string, string> TestForInstalledVersions()
|
private string GetEncoderPathFromDirectory(string path, string filename)
|
||||||
{
|
{
|
||||||
string encoderPath = null;
|
try
|
||||||
string probePath = null;
|
|
||||||
|
|
||||||
if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true))
|
|
||||||
{
|
{
|
||||||
encoderPath = _originalFFMpegPath;
|
|
||||||
probePath = _originalFFProbePath;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(encoderPath))
|
|
||||||
{
|
|
||||||
if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false))
|
|
||||||
{
|
|
||||||
encoderPath = "ffmpeg";
|
|
||||||
probePath = "ffprobe";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Tuple<string, string>(encoderPath, probePath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Tuple<string, string> GetPathsFromDirectory(string path)
|
|
||||||
{
|
|
||||||
// Since we can't predict the file extension, first try directly within the folder
|
|
||||||
// If that doesn't pan out, then do a recursive search
|
|
||||||
var files = FileSystem.GetFilePaths(path);
|
var files = FileSystem.GetFilePaths(path);
|
||||||
|
|
||||||
var excludeExtensions = new[] { ".c" };
|
var excludeExtensions = new[] { ".c" };
|
||||||
|
|
||||||
var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), filename, StringComparison.OrdinalIgnoreCase)
|
||||||
var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
&& !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
||||||
|
}
|
||||||
if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath))
|
catch (Exception)
|
||||||
{
|
{
|
||||||
files = FileSystem.GetFilePaths(path, true);
|
// Trap all exceptions, like DirNotExists, and return null
|
||||||
|
return null;
|
||||||
ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(ffmpegPath))
|
|
||||||
{
|
|
||||||
ffprobePath = GetProbePathFromEncoderPath(ffmpegPath);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return new Tuple<string, string>(ffmpegPath, ffprobePath);
|
/// <summary>
|
||||||
}
|
/// Search the system $PATH environment variable looking for given filename.
|
||||||
|
/// </summary>
|
||||||
private string GetProbePathFromEncoderPath(string appPath)
|
/// <param name="fileName"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private string ExistsOnSystemPath(string filename)
|
||||||
{
|
{
|
||||||
return FileSystem.GetFilePaths(Path.GetDirectoryName(appPath))
|
var values = Environment.GetEnvironmentVariable("PATH");
|
||||||
.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void LogPaths()
|
foreach (var path in values.Split(Path.PathSeparator))
|
||||||
{
|
{
|
||||||
_logger.LogInformation("FFMpeg: {0}", FFMpegPath ?? "not found");
|
var candidatePath = GetEncoderPathFromDirectory(path, filename);
|
||||||
_logger.LogInformation("FFProbe: {0}", FFProbePath ?? "not found");
|
|
||||||
}
|
|
||||||
|
|
||||||
private EncodingOptions GetEncodingOptions()
|
if (!string.IsNullOrEmpty(candidatePath))
|
||||||
{
|
{
|
||||||
return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
return candidatePath;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<string> _encoders = new List<string>();
|
private List<string> _encoders = new List<string>();
|
||||||
|
@ -412,12 +293,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the encoder path.
|
|
||||||
/// </summary>
|
|
||||||
/// <value>The encoder path.</value>
|
|
||||||
public string EncoderPath => FFMpegPath;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the media info.
|
/// Gets the media info.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -489,7 +364,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
|
|
||||||
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
|
// Must consume both or ffmpeg may hang due to deadlocks. See comments below.
|
||||||
RedirectStandardOutput = true,
|
RedirectStandardOutput = true,
|
||||||
FileName = FFProbePath,
|
FileName = FFprobePath,
|
||||||
Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
|
Arguments = string.Format(args, probeSizeArgument, inputPath).Trim(),
|
||||||
|
|
||||||
IsHidden = true,
|
IsHidden = true,
|
||||||
|
@ -691,7 +566,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
FileName = FFMpegPath,
|
FileName = FFmpegPath,
|
||||||
Arguments = args,
|
Arguments = args,
|
||||||
IsHidden = true,
|
IsHidden = true,
|
||||||
ErrorDialog = false,
|
ErrorDialog = false,
|
||||||
|
@ -814,7 +689,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
||||||
{
|
{
|
||||||
CreateNoWindow = true,
|
CreateNoWindow = true,
|
||||||
UseShellExecute = false,
|
UseShellExecute = false,
|
||||||
FileName = FFMpegPath,
|
FileName = FFmpegPath,
|
||||||
Arguments = args,
|
Arguments = args,
|
||||||
IsHidden = true,
|
IsHidden = true,
|
||||||
ErrorDialog = false,
|
ErrorDialog = false,
|
||||||
|
|
|
@ -8,7 +8,14 @@ namespace MediaBrowser.Model.Configuration
|
||||||
public bool EnableThrottling { get; set; }
|
public bool EnableThrottling { get; set; }
|
||||||
public int ThrottleDelaySeconds { get; set; }
|
public int ThrottleDelaySeconds { get; set; }
|
||||||
public string HardwareAccelerationType { get; set; }
|
public string HardwareAccelerationType { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// FFmpeg path as set by the user via the UI
|
||||||
|
/// </summary>
|
||||||
public string EncoderAppPath { get; set; }
|
public string EncoderAppPath { get; set; }
|
||||||
|
/// <summary>
|
||||||
|
/// The current FFmpeg path being used by the system and displayed on the transcode page
|
||||||
|
/// </summary>
|
||||||
|
public string EncoderAppPathDisplay { get; set; }
|
||||||
public string VaapiDevice { get; set; }
|
public string VaapiDevice { get; set; }
|
||||||
public int H264Crf { get; set; }
|
public int H264Crf { get; set; }
|
||||||
public string H264Preset { get; set; }
|
public string H264Preset { get; set; }
|
||||||
|
|
|
@ -4,6 +4,21 @@ using MediaBrowser.Model.Updates;
|
||||||
|
|
||||||
namespace MediaBrowser.Model.System
|
namespace MediaBrowser.Model.System
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Enum describing the location of the FFmpeg tool.
|
||||||
|
/// </summary>
|
||||||
|
public enum FFmpegLocation
|
||||||
|
{
|
||||||
|
/// <summary>No path to FFmpeg found.</summary>
|
||||||
|
NotFound,
|
||||||
|
/// <summary>Path supplied via command line using switch --ffmpeg.</summary>
|
||||||
|
SetByArgument,
|
||||||
|
/// <summary>User has supplied path via Transcoding UI page.</summary>
|
||||||
|
Custom,
|
||||||
|
/// <summary>FFmpeg tool found on system $PATH.</summary>
|
||||||
|
System
|
||||||
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Class SystemInfo
|
/// Class SystemInfo
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
@ -122,7 +137,7 @@ namespace MediaBrowser.Model.System
|
||||||
/// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
|
/// <value><c>true</c> if this instance has update available; otherwise, <c>false</c>.</value>
|
||||||
public bool HasUpdateAvailable { get; set; }
|
public bool HasUpdateAvailable { get; set; }
|
||||||
|
|
||||||
public string EncoderLocationType { get; set; }
|
public FFmpegLocation EncoderLocation { get; set; }
|
||||||
|
|
||||||
public Architecture SystemArchitecture { get; set; }
|
public Architecture SystemArchitecture { get; set; }
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user