|
@@ -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
|
|
|
|
- /// </summary>
|
|
|
|
- private readonly ILogger _logger;
|
|
|
|
-
|
|
|
|
- /// <summary>
|
|
|
|
- /// Gets the json serializer.
|
|
|
|
|
|
+ /// Gets the encoder path.
|
|
/// </summary>
|
|
/// </summary>
|
|
- /// <value>The json serializer.</value>
|
|
|
|
- private readonly IJsonSerializer _jsonSerializer;
|
|
|
|
|
|
+ /// <value>The encoder path.</value>
|
|
|
|
+ public string EncoderPath => FFmpegPath;
|
|
|
|
|
|
/// <summary>
|
|
/// <summary>
|
|
- /// The _thumbnail resource pool
|
|
|
|
|
|
+ /// The location of the discovered FFmpeg tool.
|
|
/// </summary>
|
|
/// </summary>
|
|
- private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(1, 1);
|
|
|
|
-
|
|
|
|
- public string FFMpegPath { get; private set; }
|
|
|
|
-
|
|
|
|
- public string FFProbePath { get; private set; }
|
|
|
|
|
|
+ public FFmpegLocation EncoderLocation { get; private set; }
|
|
|
|
|
|
|
|
+ private readonly ILogger _logger;
|
|
|
|
+ private readonly IJsonSerializer _jsonSerializer;
|
|
|
|
+ private string FFmpegPath;
|
|
|
|
+ private string FFprobePath;
|
|
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 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>();
|
|
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
|
- private readonly bool _hasExternalEncoder;
|
|
|
|
- private readonly string _originalFFMpegPath;
|
|
|
|
- private readonly string _originalFFProbePath;
|
|
|
|
- private readonly int DefaultImageExtractionTimeoutMs;
|
|
|
|
|
|
|
|
public MediaEncoder(
|
|
public MediaEncoder(
|
|
ILoggerFactory loggerFactory,
|
|
ILoggerFactory loggerFactory,
|
|
IJsonSerializer jsonSerializer,
|
|
IJsonSerializer jsonSerializer,
|
|
- string ffMpegPath,
|
|
|
|
- string ffProbePath,
|
|
|
|
- bool hasExternalEncoder,
|
|
|
|
|
|
+ string startupOptionsFFmpegPath,
|
|
|
|
+ string startupOptionsFFprobePath,
|
|
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)
|
|
|
|
- {
|
|
|
|
- return "External";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (string.IsNullOrWhiteSpace(FFMpegPath))
|
|
|
|
- {
|
|
|
|
- return null;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- if (IsSystemInstalledPath(FFMpegPath))
|
|
|
|
- {
|
|
|
|
- return "System";
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- return "Custom";
|
|
|
|
|
|
+ _logger.LogWarning("--ffprobe switch is deprecated and shall be removed in the next release");
|
|
}
|
|
}
|
|
- }
|
|
|
|
|
|
|
|
- private bool IsSystemInstalledPath(string path)
|
|
|
|
- {
|
|
|
|
- if (path.IndexOf("/", StringComparison.Ordinal) == -1 && path.IndexOf("\\", StringComparison.Ordinal) == -1)
|
|
|
|
|
|
+ // 1) Custom path stored in config/encoding xml file under tag <EncoderAppPath> takes precedence
|
|
|
|
+ if (!ValidatePath(ConfigurationManager.GetConfiguration<EncodingOptions>("encoding").EncoderAppPath, FFmpegLocation.Custom))
|
|
{
|
|
{
|
|
- return true;
|
|
|
|
|
|
+ // 2) Check if the --ffmpeg CLI switch has been given
|
|
|
|
+ if (!ValidatePath(StartupOptionFFmpegPath, FFmpegLocation.SetByArgument))
|
|
|
|
+ {
|
|
|
|
+ // 3) Search system $PATH environment variable for valid FFmpeg
|
|
|
|
+ if (!ValidatePath(ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System))
|
|
|
|
+ {
|
|
|
|
+ EncoderLocation = FFmpegLocation.NotFound;
|
|
|
|
+ FFmpegPath = null;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
}
|
|
}
|
|
|
|
|
|
- return false;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- public void Init()
|
|
|
|
- {
|
|
|
|
- InitPaths();
|
|
|
|
|
|
+ // Write the FFmpeg path to the config/encoding.xml file as <EncoderAppPathDisplay> so it appears in UI
|
|
|
|
+ var config = ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
|
|
|
+ config.EncoderAppPathDisplay = FFmpegPath ?? string.Empty;
|
|
|
|
+ ConfigurationManager.SaveConfiguration("encoding", config);
|
|
|
|
|
|
- if (!string.IsNullOrWhiteSpace(FFMpegPath))
|
|
|
|
|
|
+ // Only if mpeg path is set, try and set path to probe
|
|
|
|
+ if (FFmpegPath != null)
|
|
{
|
|
{
|
|
- var result = new EncoderValidator(_logger, _processFactory).Validate(FFMpegPath);
|
|
|
|
|
|
+ // Determine a probe path from the mpeg path
|
|
|
|
+ FFprobePath = Regex.Replace(FFmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1");
|
|
|
|
+
|
|
|
|
+ // Interrogate to understand what coders are supported
|
|
|
|
+ var result = new EncoderValidator(_logger, _processFactory).GetAvailableCoders(FFmpegPath);
|
|
|
|
|
|
SetAvailableDecoders(result.decoders);
|
|
SetAvailableDecoders(result.decoders);
|
|
SetAvailableEncoders(result.encoders);
|
|
SetAvailableEncoders(result.encoders);
|
|
}
|
|
}
|
|
- }
|
|
|
|
-
|
|
|
|
- private void InitPaths()
|
|
|
|
- {
|
|
|
|
- ConfigureEncoderPaths();
|
|
|
|
-
|
|
|
|
- if (_hasExternalEncoder)
|
|
|
|
- {
|
|
|
|
- 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);
|
|
|
|
- }
|
|
|
|
|
|
+ _logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation.ToString(), FFmpegPath ?? string.Empty);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Triggered from the Settings > Transcoding UI page when users submits Custom FFmpeg path to use.
|
|
|
|
+ /// Only write the new path to xml if it exists. Do not perform validation checks on ffmpeg here.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="path"></param>
|
|
|
|
+ /// <param name="pathType"></param>
|
|
public void UpdateEncoderPath(string path, string pathType)
|
|
public void UpdateEncoderPath(string path, string pathType)
|
|
{
|
|
{
|
|
- if (_hasExternalEncoder)
|
|
|
|
- {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
|
|
+ string newPath;
|
|
|
|
|
|
_logger.LogInformation("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty);
|
|
_logger.LogInformation("Attempting to update encoder path to {0}. pathType: {1}", path ?? string.Empty, pathType ?? string.Empty);
|
|
|
|
|
|
- Tuple<string, string> newPaths;
|
|
|
|
-
|
|
|
|
- if (string.Equals(pathType, "system", StringComparison.OrdinalIgnoreCase))
|
|
|
|
- {
|
|
|
|
- path = "ffmpeg";
|
|
|
|
-
|
|
|
|
- newPaths = TestForInstalledVersions();
|
|
|
|
- }
|
|
|
|
- else if (string.Equals(pathType, "custom", StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
|
+ 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);
|
|
|
|
|
|
+ throw new ArgumentException("Unexpected pathType value");
|
|
}
|
|
}
|
|
- else
|
|
|
|
|
|
+ else if (string.IsNullOrWhiteSpace(path))
|
|
{
|
|
{
|
|
- throw new ArgumentException("Unexpected pathType value");
|
|
|
|
|
|
+ // User had cleared the custom path in UI
|
|
|
|
+ newPath = string.Empty;
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (string.IsNullOrWhiteSpace(newPaths.Item1))
|
|
|
|
|
|
+ else if (File.Exists(path))
|
|
{
|
|
{
|
|
- throw new ResourceNotFoundException("ffmpeg not found");
|
|
|
|
|
|
+ newPath = path;
|
|
}
|
|
}
|
|
- if (string.IsNullOrWhiteSpace(newPaths.Item2))
|
|
|
|
|
|
+ else if (Directory.Exists(path))
|
|
{
|
|
{
|
|
- throw new ResourceNotFoundException("ffprobe not found");
|
|
|
|
|
|
+ // Given path is directory, so resolve down to filename
|
|
|
|
+ newPath = GetEncoderPathFromDirectory(path, "ffmpeg");
|
|
}
|
|
}
|
|
-
|
|
|
|
- path = newPaths.Item1;
|
|
|
|
-
|
|
|
|
- if (!ValidateVersion(path, true))
|
|
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- throw new ResourceNotFoundException("ffmpeg version 3.0 or greater is required.");
|
|
|
|
|
|
+ throw new ResourceNotFoundException();
|
|
}
|
|
}
|
|
|
|
|
|
- var config = GetEncodingOptions();
|
|
|
|
- config.EncoderAppPath = path;
|
|
|
|
|
|
+ // Write the new ffmpeg path to the xml as <EncoderAppPath>
|
|
|
|
+ // This ensures its not lost on next startup
|
|
|
|
+ var config = ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
|
|
|
+ config.EncoderAppPath = newPath;
|
|
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)
|
|
|
|
- {
|
|
|
|
- return new EncoderValidator(_logger, _processFactory).ValidateVersion(path, logOutput);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void ConfigureEncoderPaths()
|
|
|
|
|
|
+ /// <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)
|
|
{
|
|
{
|
|
- if (_hasExternalEncoder)
|
|
|
|
- {
|
|
|
|
- return;
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- var appPath = GetEncodingOptions().EncoderAppPath;
|
|
|
|
|
|
+ bool rc = false;
|
|
|
|
|
|
- if (string.IsNullOrWhiteSpace(appPath))
|
|
|
|
|
|
+ if (!string.IsNullOrEmpty(path))
|
|
{
|
|
{
|
|
- appPath = Path.Combine(ConfigurationManager.ApplicationPaths.ProgramDataPath, "ffmpeg");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- 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;
|
|
|
|
- }
|
|
|
|
|
|
+ if (File.Exists(path))
|
|
|
|
+ {
|
|
|
|
+ rc = new EncoderValidator(_logger, _processFactory).ValidateVersion(path, true);
|
|
|
|
|
|
- LogPaths();
|
|
|
|
- }
|
|
|
|
|
|
+ if (!rc)
|
|
|
|
+ {
|
|
|
|
+ _logger.LogWarning("FFmpeg: {0}: Failed version check: {1}", location.ToString(), path);
|
|
|
|
+ }
|
|
|
|
|
|
- private Tuple<string, string> GetEncoderPaths(string configuredPath)
|
|
|
|
- {
|
|
|
|
- var appPath = configuredPath;
|
|
|
|
|
|
+ // ToDo - Enable the ffmpeg validator. At the moment any version can be used.
|
|
|
|
+ rc = true;
|
|
|
|
|
|
- if (!string.IsNullOrWhiteSpace(appPath))
|
|
|
|
- {
|
|
|
|
- if (Directory.Exists(appPath))
|
|
|
|
- {
|
|
|
|
- return GetPathsFromDirectory(appPath);
|
|
|
|
|
|
+ FFmpegPath = path;
|
|
|
|
+ EncoderLocation = location;
|
|
}
|
|
}
|
|
-
|
|
|
|
- if (File.Exists(appPath))
|
|
|
|
|
|
+ else
|
|
{
|
|
{
|
|
- return new Tuple<string, string>(appPath, GetProbePathFromEncoderPath(appPath));
|
|
|
|
|
|
+ _logger.LogWarning("FFmpeg: {0}: File not found: {1}", location.ToString(), path);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
- return new Tuple<string, string>(null, null);
|
|
|
|
|
|
+ return rc;
|
|
}
|
|
}
|
|
|
|
|
|
- private Tuple<string, string> TestForInstalledVersions()
|
|
|
|
|
|
+ private string GetEncoderPathFromDirectory(string path, string filename)
|
|
{
|
|
{
|
|
- string encoderPath = null;
|
|
|
|
- string probePath = null;
|
|
|
|
-
|
|
|
|
- if (_hasExternalEncoder && ValidateVersion(_originalFFMpegPath, true))
|
|
|
|
|
|
+ try
|
|
{
|
|
{
|
|
- encoderPath = _originalFFMpegPath;
|
|
|
|
- probePath = _originalFFProbePath;
|
|
|
|
- }
|
|
|
|
|
|
+ var files = FileSystem.GetFilePaths(path);
|
|
|
|
|
|
- if (string.IsNullOrWhiteSpace(encoderPath))
|
|
|
|
|
|
+ var excludeExtensions = new[] { ".c" };
|
|
|
|
+
|
|
|
|
+ return files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), filename, StringComparison.OrdinalIgnoreCase)
|
|
|
|
+ && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
|
|
|
+ }
|
|
|
|
+ catch (Exception)
|
|
{
|
|
{
|
|
- if (ValidateVersion("ffmpeg", true) && ValidateVersion("ffprobe", false))
|
|
|
|
- {
|
|
|
|
- encoderPath = "ffmpeg";
|
|
|
|
- probePath = "ffprobe";
|
|
|
|
- }
|
|
|
|
|
|
+ // Trap all exceptions, like DirNotExists, and return null
|
|
|
|
+ return null;
|
|
}
|
|
}
|
|
-
|
|
|
|
- return new Tuple<string, string>(encoderPath, probePath);
|
|
|
|
}
|
|
}
|
|
|
|
|
|
- private Tuple<string, string> GetPathsFromDirectory(string path)
|
|
|
|
|
|
+ /// <summary>
|
|
|
|
+ /// Search the system $PATH environment variable looking for given filename.
|
|
|
|
+ /// </summary>
|
|
|
|
+ /// <param name="fileName"></param>
|
|
|
|
+ /// <returns></returns>
|
|
|
|
+ private string ExistsOnSystemPath(string filename)
|
|
{
|
|
{
|
|
- // 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 excludeExtensions = new[] { ".c" };
|
|
|
|
|
|
+ var values = Environment.GetEnvironmentVariable("PATH");
|
|
|
|
|
|
- var ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
|
|
|
- var ffprobePath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
|
|
|
-
|
|
|
|
- if (string.IsNullOrWhiteSpace(ffmpegPath) || !File.Exists(ffmpegPath))
|
|
|
|
|
|
+ foreach (var path in values.Split(Path.PathSeparator))
|
|
{
|
|
{
|
|
- files = FileSystem.GetFilePaths(path, true);
|
|
|
|
-
|
|
|
|
- ffmpegPath = files.FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffmpeg", StringComparison.OrdinalIgnoreCase) && !excludeExtensions.Contains(Path.GetExtension(i) ?? string.Empty));
|
|
|
|
|
|
+ var candidatePath = GetEncoderPathFromDirectory(path, filename);
|
|
|
|
|
|
- if (!string.IsNullOrWhiteSpace(ffmpegPath))
|
|
|
|
|
|
+ if (!string.IsNullOrEmpty(candidatePath))
|
|
{
|
|
{
|
|
- ffprobePath = GetProbePathFromEncoderPath(ffmpegPath);
|
|
|
|
|
|
+ return candidatePath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-
|
|
|
|
- return new Tuple<string, string>(ffmpegPath, ffprobePath);
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private string GetProbePathFromEncoderPath(string appPath)
|
|
|
|
- {
|
|
|
|
- return FileSystem.GetFilePaths(Path.GetDirectoryName(appPath))
|
|
|
|
- .FirstOrDefault(i => string.Equals(Path.GetFileNameWithoutExtension(i), "ffprobe", StringComparison.OrdinalIgnoreCase));
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private void LogPaths()
|
|
|
|
- {
|
|
|
|
- _logger.LogInformation("FFMpeg: {0}", FFMpegPath ?? "not found");
|
|
|
|
- _logger.LogInformation("FFProbe: {0}", FFProbePath ?? "not found");
|
|
|
|
- }
|
|
|
|
-
|
|
|
|
- private EncodingOptions GetEncodingOptions()
|
|
|
|
- {
|
|
|
|
- return ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
|
|
|
|
|
+ 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,10 +566,11 @@ 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,
|
|
|
|
+ EnableRaisingEvents = true
|
|
});
|
|
});
|
|
|
|
|
|
_logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
|
_logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
|
@@ -813,10 +689,11 @@ 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,
|
|
|
|
+ EnableRaisingEvents = true
|
|
});
|
|
});
|
|
|
|
|
|
_logger.LogInformation(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
|
|
_logger.LogInformation(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
|