|
|
@ -3,13 +3,13 @@ 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.Json;
|
|
|
|
using System.Text.RegularExpressions;
|
|
|
|
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.Controller.Configuration;
|
|
|
|
using MediaBrowser.Controller.Configuration;
|
|
|
|
using MediaBrowser.Controller.Library;
|
|
|
|
|
|
|
|
using MediaBrowser.Controller.MediaEncoding;
|
|
|
|
using MediaBrowser.Controller.MediaEncoding;
|
|
|
|
using MediaBrowser.MediaEncoding.Probing;
|
|
|
|
using MediaBrowser.MediaEncoding.Probing;
|
|
|
|
using MediaBrowser.Model.Configuration;
|
|
|
|
using MediaBrowser.Model.Configuration;
|
|
|
@ -19,9 +19,9 @@ using MediaBrowser.Model.Entities;
|
|
|
|
using MediaBrowser.Model.Globalization;
|
|
|
|
using MediaBrowser.Model.Globalization;
|
|
|
|
using MediaBrowser.Model.IO;
|
|
|
|
using MediaBrowser.Model.IO;
|
|
|
|
using MediaBrowser.Model.MediaInfo;
|
|
|
|
using MediaBrowser.Model.MediaInfo;
|
|
|
|
using MediaBrowser.Model.Serialization;
|
|
|
|
|
|
|
|
using MediaBrowser.Model.System;
|
|
|
|
using MediaBrowser.Model.System;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
|
|
|
|
|
|
|
|
namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -31,55 +31,60 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
public class MediaEncoder : IMediaEncoder, IDisposable
|
|
|
|
public class MediaEncoder : IMediaEncoder, IDisposable
|
|
|
|
{
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Gets the encoder path.
|
|
|
|
/// The default image extraction timeout in milliseconds.
|
|
|
|
/// </summary>
|
|
|
|
/// </summary>
|
|
|
|
/// <value>The encoder path.</value>
|
|
|
|
internal const int DefaultImageExtractionTimeout = 5000;
|
|
|
|
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;
|
|
|
|
private readonly IJsonSerializer _jsonSerializer;
|
|
|
|
private readonly IServerConfigurationManager _configurationManager;
|
|
|
|
private string FFmpegPath;
|
|
|
|
private readonly IFileSystem _fileSystem;
|
|
|
|
private string FFprobePath;
|
|
|
|
|
|
|
|
protected readonly IServerConfigurationManager ConfigurationManager;
|
|
|
|
|
|
|
|
protected readonly IFileSystem FileSystem;
|
|
|
|
|
|
|
|
protected readonly Func<ISubtitleEncoder> SubtitleEncoder;
|
|
|
|
|
|
|
|
protected readonly Func<IMediaSourceManager> MediaSourceManager;
|
|
|
|
|
|
|
|
private readonly IProcessFactory _processFactory;
|
|
|
|
private readonly IProcessFactory _processFactory;
|
|
|
|
private readonly int DefaultImageExtractionTimeoutMs;
|
|
|
|
private readonly ILocalizationManager _localization;
|
|
|
|
private readonly string StartupOptionFFmpegPath;
|
|
|
|
private readonly Func<ISubtitleEncoder> _subtitleEncoder;
|
|
|
|
|
|
|
|
private readonly IConfiguration _configuration;
|
|
|
|
|
|
|
|
private readonly string _startupOptionFFmpegPath;
|
|
|
|
|
|
|
|
|
|
|
|
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
|
|
|
|
private readonly SemaphoreSlim _thumbnailResourcePool = new SemaphoreSlim(2, 2);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private readonly object _runningProcessesLock = new object();
|
|
|
|
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
|
|
|
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
|
|
|
|
private readonly ILocalizationManager _localization;
|
|
|
|
|
|
|
|
|
|
|
|
private EncodingHelper _encodingHelper;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private string _ffmpegPath;
|
|
|
|
|
|
|
|
private string _ffprobePath;
|
|
|
|
|
|
|
|
|
|
|
|
public MediaEncoder(
|
|
|
|
public MediaEncoder(
|
|
|
|
ILoggerFactory loggerFactory,
|
|
|
|
ILogger<MediaEncoder> logger,
|
|
|
|
IJsonSerializer jsonSerializer,
|
|
|
|
|
|
|
|
string startupOptionsFFmpegPath,
|
|
|
|
|
|
|
|
IServerConfigurationManager configurationManager,
|
|
|
|
IServerConfigurationManager configurationManager,
|
|
|
|
IFileSystem fileSystem,
|
|
|
|
IFileSystem fileSystem,
|
|
|
|
Func<ISubtitleEncoder> subtitleEncoder,
|
|
|
|
|
|
|
|
Func<IMediaSourceManager> mediaSourceManager,
|
|
|
|
|
|
|
|
IProcessFactory processFactory,
|
|
|
|
IProcessFactory processFactory,
|
|
|
|
int defaultImageExtractionTimeoutMs,
|
|
|
|
ILocalizationManager localization,
|
|
|
|
ILocalizationManager localization)
|
|
|
|
Func<ISubtitleEncoder> subtitleEncoder,
|
|
|
|
{
|
|
|
|
IConfiguration configuration,
|
|
|
|
_logger = loggerFactory.CreateLogger(nameof(MediaEncoder));
|
|
|
|
string startupOptionsFFmpegPath)
|
|
|
|
_jsonSerializer = jsonSerializer;
|
|
|
|
{
|
|
|
|
StartupOptionFFmpegPath = startupOptionsFFmpegPath;
|
|
|
|
_logger = logger;
|
|
|
|
ConfigurationManager = configurationManager;
|
|
|
|
_configurationManager = configurationManager;
|
|
|
|
FileSystem = fileSystem;
|
|
|
|
_fileSystem = fileSystem;
|
|
|
|
SubtitleEncoder = subtitleEncoder;
|
|
|
|
|
|
|
|
_processFactory = processFactory;
|
|
|
|
_processFactory = processFactory;
|
|
|
|
DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
|
|
|
|
|
|
|
|
_localization = localization;
|
|
|
|
_localization = localization;
|
|
|
|
|
|
|
|
_startupOptionFFmpegPath = startupOptionsFFmpegPath;
|
|
|
|
|
|
|
|
_subtitleEncoder = subtitleEncoder;
|
|
|
|
|
|
|
|
_configuration = configuration;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private EncodingHelper EncodingHelper
|
|
|
|
|
|
|
|
=> LazyInitializer.EnsureInitialized(
|
|
|
|
|
|
|
|
ref _encodingHelper,
|
|
|
|
|
|
|
|
() => new EncodingHelper(this, _fileSystem, _subtitleEncoder(), _configuration));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
|
|
public string EncoderPath => _ffmpegPath;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <inheritdoc />
|
|
|
|
|
|
|
|
public FFmpegLocation EncoderLocation { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
|
/// Run at startup or if the user removes a Custom path from transcode page.
|
|
|
|
/// Run at startup or if the user removes a Custom path from transcode page.
|
|
|
|
/// Sets global variables FFmpegPath.
|
|
|
|
/// Sets global variables FFmpegPath.
|
|
|
@ -88,39 +93,39 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
public void SetFFmpegPath()
|
|
|
|
public void SetFFmpegPath()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 1) Custom path stored in config/encoding xml file under tag <EncoderAppPath> takes precedence
|
|
|
|
// 1) Custom path stored in config/encoding xml file under tag <EncoderAppPath> takes precedence
|
|
|
|
if (!ValidatePath(ConfigurationManager.GetConfiguration<EncodingOptions>("encoding").EncoderAppPath, FFmpegLocation.Custom))
|
|
|
|
if (!ValidatePath(_configurationManager.GetConfiguration<EncodingOptions>("encoding").EncoderAppPath, FFmpegLocation.Custom))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 2) Check if the --ffmpeg CLI switch has been given
|
|
|
|
// 2) Check if the --ffmpeg CLI switch has been given
|
|
|
|
if (!ValidatePath(StartupOptionFFmpegPath, FFmpegLocation.SetByArgument))
|
|
|
|
if (!ValidatePath(_startupOptionFFmpegPath, FFmpegLocation.SetByArgument))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// 3) Search system $PATH environment variable for valid FFmpeg
|
|
|
|
// 3) Search system $PATH environment variable for valid FFmpeg
|
|
|
|
if (!ValidatePath(ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System))
|
|
|
|
if (!ValidatePath(ExistsOnSystemPath("ffmpeg"), FFmpegLocation.System))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
EncoderLocation = FFmpegLocation.NotFound;
|
|
|
|
EncoderLocation = FFmpegLocation.NotFound;
|
|
|
|
FFmpegPath = null;
|
|
|
|
_ffmpegPath = null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Write the FFmpeg path to the config/encoding.xml file as <EncoderAppPathDisplay> so it appears in UI
|
|
|
|
// Write the FFmpeg path to the config/encoding.xml file as <EncoderAppPathDisplay> so it appears in UI
|
|
|
|
var config = ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
|
|
|
var config = _configurationManager.GetConfiguration<EncodingOptions>("encoding");
|
|
|
|
config.EncoderAppPathDisplay = FFmpegPath ?? string.Empty;
|
|
|
|
config.EncoderAppPathDisplay = _ffmpegPath ?? string.Empty;
|
|
|
|
ConfigurationManager.SaveConfiguration("encoding", config);
|
|
|
|
_configurationManager.SaveConfiguration("encoding", config);
|
|
|
|
|
|
|
|
|
|
|
|
// Only if mpeg path is set, try and set path to probe
|
|
|
|
// Only if mpeg path is set, try and set path to probe
|
|
|
|
if (FFmpegPath != null)
|
|
|
|
if (_ffmpegPath != null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
// Determine a probe path from the mpeg path
|
|
|
|
// Determine a probe path from the mpeg path
|
|
|
|
FFprobePath = Regex.Replace(FFmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1");
|
|
|
|
_ffprobePath = Regex.Replace(_ffmpegPath, @"[^\/\\]+?(\.[^\/\\\n.]+)?$", @"ffprobe$1");
|
|
|
|
|
|
|
|
|
|
|
|
// Interrogate to understand what coders are supported
|
|
|
|
// Interrogate to understand what coders are supported
|
|
|
|
var validator = new EncoderValidator(_logger, FFmpegPath);
|
|
|
|
var validator = new EncoderValidator(_logger, _ffmpegPath);
|
|
|
|
|
|
|
|
|
|
|
|
SetAvailableDecoders(validator.GetDecoders());
|
|
|
|
SetAvailableDecoders(validator.GetDecoders());
|
|
|
|
SetAvailableEncoders(validator.GetEncoders());
|
|
|
|
SetAvailableEncoders(validator.GetEncoders());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation, FFmpegPath ?? string.Empty);
|
|
|
|
_logger.LogInformation("FFmpeg: {0}: {1}", EncoderLocation, _ffmpegPath ?? string.Empty);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
@ -160,9 +165,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
// Write the new ffmpeg path to the xml as <EncoderAppPath>
|
|
|
|
// Write the new ffmpeg path to the xml as <EncoderAppPath>
|
|
|
|
// This ensures its not lost on next startup
|
|
|
|
// This ensures its not lost on next startup
|
|
|
|
var config = ConfigurationManager.GetConfiguration<EncodingOptions>("encoding");
|
|
|
|
var config = _configurationManager.GetConfiguration<EncodingOptions>("encoding");
|
|
|
|
config.EncoderAppPath = newPath;
|
|
|
|
config.EncoderAppPath = newPath;
|
|
|
|
ConfigurationManager.SaveConfiguration("encoding", config);
|
|
|
|
_configurationManager.SaveConfiguration("encoding", config);
|
|
|
|
|
|
|
|
|
|
|
|
// Trigger SetFFmpegPath so we validate the new path and setup probe path
|
|
|
|
// Trigger SetFFmpegPath so we validate the new path and setup probe path
|
|
|
|
SetFFmpegPath();
|
|
|
|
SetFFmpegPath();
|
|
|
@ -193,7 +198,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
// ToDo - Enable the ffmpeg validator. At the moment any version can be used.
|
|
|
|
// ToDo - Enable the ffmpeg validator. At the moment any version can be used.
|
|
|
|
rc = true;
|
|
|
|
rc = true;
|
|
|
|
|
|
|
|
|
|
|
|
FFmpegPath = path;
|
|
|
|
_ffmpegPath = path;
|
|
|
|
EncoderLocation = location;
|
|
|
|
EncoderLocation = location;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
else
|
|
|
@ -209,7 +214,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
{
|
|
|
|
{
|
|
|
|
try
|
|
|
|
try
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var files = FileSystem.GetFilePaths(path);
|
|
|
|
var files = _fileSystem.GetFilePaths(path);
|
|
|
|
|
|
|
|
|
|
|
|
var excludeExtensions = new[] { ".c" };
|
|
|
|
var excludeExtensions = new[] { ".c" };
|
|
|
|
|
|
|
|
|
|
|
@ -304,7 +309,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
|
|
|
|
var extractChapters = request.MediaType == DlnaProfileType.Video && request.ExtractChapters;
|
|
|
|
|
|
|
|
|
|
|
|
var inputFiles = MediaEncoderHelpers.GetInputArgument(FileSystem, request.MediaSource.Path, request.MountedIso, request.PlayableStreamFileNames);
|
|
|
|
var inputFiles = MediaEncoderHelpers.GetInputArgument(_fileSystem, request.MediaSource.Path, request.MountedIso, request.PlayableStreamFileNames);
|
|
|
|
|
|
|
|
|
|
|
|
var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length);
|
|
|
|
var probeSize = EncodingHelper.GetProbeSizeArgument(inputFiles.Length);
|
|
|
|
string analyzeDuration;
|
|
|
|
string analyzeDuration;
|
|
|
@ -365,7 +370,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 = args,
|
|
|
|
Arguments = args,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@ -383,7 +388,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
_logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
|
|
|
_logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
using (var processWrapper = new ProcessWrapper(process, this, _logger))
|
|
|
|
using (var processWrapper = new ProcessWrapper(process, this))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_logger.LogDebug("Starting ffprobe with args {Args}", args);
|
|
|
|
_logger.LogDebug("Starting ffprobe with args {Args}", args);
|
|
|
|
StartProcess(processWrapper);
|
|
|
|
StartProcess(processWrapper);
|
|
|
@ -391,7 +396,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
InternalMediaInfoResult result;
|
|
|
|
InternalMediaInfoResult result;
|
|
|
|
try
|
|
|
|
try
|
|
|
|
{
|
|
|
|
{
|
|
|
|
result = await _jsonSerializer.DeserializeFromStreamAsync<InternalMediaInfoResult>(
|
|
|
|
result = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(
|
|
|
|
process.StandardOutput.BaseStream).ConfigureAwait(false);
|
|
|
|
process.StandardOutput.BaseStream).ConfigureAwait(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
catch
|
|
|
@ -423,7 +428,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new ProbeResultNormalizer(_logger, FileSystem, _localization).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
|
|
|
|
return new ProbeResultNormalizer(_logger, _fileSystem, _localization).GetMediaInfo(result, videoType, isAudio, primaryPath, protocol);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -486,7 +491,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
throw new ArgumentNullException(nameof(inputPath));
|
|
|
|
throw new ArgumentNullException(nameof(inputPath));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var tempExtractPath = Path.Combine(ConfigurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg");
|
|
|
|
var tempExtractPath = Path.Combine(_configurationManager.ApplicationPaths.TempDirectory, Guid.NewGuid() + ".jpg");
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
|
|
|
|
Directory.CreateDirectory(Path.GetDirectoryName(tempExtractPath));
|
|
|
|
|
|
|
|
|
|
|
|
// apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
|
|
|
|
// apply some filters to thumbnail extracted below (below) crop any black lines that we made and get the correct ar then scale to width 600.
|
|
|
@ -545,7 +550,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args;
|
|
|
|
args = string.Format("-ss {0} ", GetTimeParameter(offset.Value)) + args;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var encodinghelper = new EncodingHelper(this, FileSystem, SubtitleEncoder());
|
|
|
|
|
|
|
|
if (videoStream != null)
|
|
|
|
if (videoStream != null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
/* fix
|
|
|
|
/* fix
|
|
|
@ -559,7 +563,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(container))
|
|
|
|
if (!string.IsNullOrWhiteSpace(container))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var inputFormat = encodinghelper.GetInputFormat(container);
|
|
|
|
var inputFormat = EncodingHelper.GetInputFormat(container);
|
|
|
|
if (!string.IsNullOrWhiteSpace(inputFormat))
|
|
|
|
if (!string.IsNullOrWhiteSpace(inputFormat))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
args = "-f " + inputFormat + " " + args;
|
|
|
|
args = "-f " + inputFormat + " " + args;
|
|
|
@ -570,7 +574,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,
|
|
|
@ -579,7 +583,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
_logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
|
|
|
_logger.LogDebug("{0} {1}", process.StartInfo.FileName, process.StartInfo.Arguments);
|
|
|
|
|
|
|
|
|
|
|
|
using (var processWrapper = new ProcessWrapper(process, this, _logger))
|
|
|
|
using (var processWrapper = new ProcessWrapper(process, this))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
bool ranToCompletion;
|
|
|
|
bool ranToCompletion;
|
|
|
|
|
|
|
|
|
|
|
@ -588,10 +592,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
{
|
|
|
|
{
|
|
|
|
StartProcess(processWrapper);
|
|
|
|
StartProcess(processWrapper);
|
|
|
|
|
|
|
|
|
|
|
|
var timeoutMs = ConfigurationManager.Configuration.ImageExtractionTimeoutMs;
|
|
|
|
var timeoutMs = _configurationManager.Configuration.ImageExtractionTimeoutMs;
|
|
|
|
if (timeoutMs <= 0)
|
|
|
|
if (timeoutMs <= 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
timeoutMs = DefaultImageExtractionTimeoutMs;
|
|
|
|
timeoutMs = DefaultImageExtractionTimeout;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
|
|
|
|
ranToCompletion = await process.WaitForExitAsync(timeoutMs).ConfigureAwait(false);
|
|
|
@ -607,7 +611,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
|
|
|
var exitCode = ranToCompletion ? processWrapper.ExitCode ?? 0 : -1;
|
|
|
|
var file = FileSystem.GetFileInfo(tempExtractPath);
|
|
|
|
var file = _fileSystem.GetFileInfo(tempExtractPath);
|
|
|
|
|
|
|
|
|
|
|
|
if (exitCode == -1 || !file.Exists || file.Length == 0)
|
|
|
|
if (exitCode == -1 || !file.Exists || file.Length == 0)
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -675,7 +679,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
args = analyzeDurationArgument + " " + args;
|
|
|
|
args = analyzeDurationArgument + " " + args;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var encodinghelper = new EncodingHelper(this, FileSystem, SubtitleEncoder());
|
|
|
|
|
|
|
|
if (videoStream != null)
|
|
|
|
if (videoStream != null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
/* fix
|
|
|
|
/* fix
|
|
|
@ -689,7 +692,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(container))
|
|
|
|
if (!string.IsNullOrWhiteSpace(container))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var inputFormat = encodinghelper.GetInputFormat(container);
|
|
|
|
var inputFormat = EncodingHelper.GetInputFormat(container);
|
|
|
|
if (!string.IsNullOrWhiteSpace(inputFormat))
|
|
|
|
if (!string.IsNullOrWhiteSpace(inputFormat))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
args = "-f " + inputFormat + " " + args;
|
|
|
|
args = "-f " + inputFormat + " " + args;
|
|
|
@ -700,7 +703,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,
|
|
|
@ -713,7 +716,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
bool ranToCompletion = false;
|
|
|
|
bool ranToCompletion = false;
|
|
|
|
|
|
|
|
|
|
|
|
using (var processWrapper = new ProcessWrapper(process, this, _logger))
|
|
|
|
using (var processWrapper = new ProcessWrapper(process, this))
|
|
|
|
{
|
|
|
|
{
|
|
|
|
try
|
|
|
|
try
|
|
|
|
{
|
|
|
|
{
|
|
|
@ -736,10 +739,10 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
cancellationToken.ThrowIfCancellationRequested();
|
|
|
|
|
|
|
|
|
|
|
|
var jpegCount = FileSystem.GetFilePaths(targetDirectory)
|
|
|
|
var jpegCount = _fileSystem.GetFilePaths(targetDirectory)
|
|
|
|
.Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
|
|
|
|
.Count(i => string.Equals(Path.GetExtension(i), ".jpg", StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
|
|
|
|
|
|
isResponsive = (jpegCount > lastCount);
|
|
|
|
isResponsive = jpegCount > lastCount;
|
|
|
|
lastCount = jpegCount;
|
|
|
|
lastCount = jpegCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
@ -770,7 +773,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
{
|
|
|
|
{
|
|
|
|
process.Process.Start();
|
|
|
|
process.Process.Start();
|
|
|
|
|
|
|
|
|
|
|
|
lock (_runningProcesses)
|
|
|
|
lock (_runningProcessesLock)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_runningProcesses.Add(process);
|
|
|
|
_runningProcesses.Add(process);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -804,7 +807,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
private void StopProcesses()
|
|
|
|
private void StopProcesses()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
List<ProcessWrapper> proceses;
|
|
|
|
List<ProcessWrapper> proceses;
|
|
|
|
lock (_runningProcesses)
|
|
|
|
lock (_runningProcessesLock)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
proceses = _runningProcesses.ToList();
|
|
|
|
proceses = _runningProcesses.ToList();
|
|
|
|
_runningProcesses.Clear();
|
|
|
|
_runningProcesses.Clear();
|
|
|
@ -827,12 +830,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
return path.Replace('\\', '/').Replace(":", "\\:").Replace("'", "'\\\\\\''");
|
|
|
|
return path.Replace('\\', '/').Replace(":", "\\:").Replace("'", "'\\\\\\''");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <inheritdoc />
|
|
|
|
/// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
|
|
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Dispose(true);
|
|
|
|
Dispose(true);
|
|
|
|
|
|
|
|
GC.SuppressFinalize(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// <summary>
|
|
|
@ -852,11 +854,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
throw new NotImplementedException();
|
|
|
|
throw new NotImplementedException();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public string[] GetPlayableStreamFileNames(string path, VideoType videoType)
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
|
|
throw new NotImplementedException();
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber)
|
|
|
|
public IEnumerable<string> GetPrimaryPlaylistVobFiles(string path, IIsoMount isoMount, uint? titleNumber)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
throw new NotImplementedException();
|
|
|
|
throw new NotImplementedException();
|
|
|
@ -870,21 +867,24 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
private class ProcessWrapper : IDisposable
|
|
|
|
private class ProcessWrapper : IDisposable
|
|
|
|
{
|
|
|
|
{
|
|
|
|
public readonly IProcess Process;
|
|
|
|
|
|
|
|
public bool HasExited;
|
|
|
|
|
|
|
|
public int? ExitCode;
|
|
|
|
|
|
|
|
private readonly MediaEncoder _mediaEncoder;
|
|
|
|
private readonly MediaEncoder _mediaEncoder;
|
|
|
|
private readonly ILogger _logger;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder, ILogger logger)
|
|
|
|
private bool _disposed = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public ProcessWrapper(IProcess process, MediaEncoder mediaEncoder)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
Process = process;
|
|
|
|
Process = process;
|
|
|
|
_mediaEncoder = mediaEncoder;
|
|
|
|
_mediaEncoder = mediaEncoder;
|
|
|
|
_logger = logger;
|
|
|
|
Process.Exited += OnProcessExited;
|
|
|
|
Process.Exited += Process_Exited;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void Process_Exited(object sender, EventArgs e)
|
|
|
|
public IProcess Process { get; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public bool HasExited { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public int? ExitCode { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void OnProcessExited(object sender, EventArgs e)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
var process = (IProcess)sender;
|
|
|
|
var process = (IProcess)sender;
|
|
|
|
|
|
|
|
|
|
|
@ -903,7 +903,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
|
|
|
|
|
|
|
|
private void DisposeProcess(IProcess process)
|
|
|
|
private void DisposeProcess(IProcess process)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
lock (_mediaEncoder._runningProcesses)
|
|
|
|
lock (_mediaEncoder._runningProcessesLock)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
_mediaEncoder._runningProcesses.Remove(this);
|
|
|
|
_mediaEncoder._runningProcesses.Remove(this);
|
|
|
|
}
|
|
|
|
}
|
|
|
@ -917,23 +917,18 @@ namespace MediaBrowser.MediaEncoding.Encoder
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private bool _disposed;
|
|
|
|
|
|
|
|
private readonly object _syncLock = new object();
|
|
|
|
|
|
|
|
public void Dispose()
|
|
|
|
public void Dispose()
|
|
|
|
{
|
|
|
|
{
|
|
|
|
lock (_syncLock)
|
|
|
|
if (!_disposed)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (!_disposed)
|
|
|
|
if (Process != null)
|
|
|
|
{
|
|
|
|
{
|
|
|
|
if (Process != null)
|
|
|
|
Process.Exited -= OnProcessExited;
|
|
|
|
{
|
|
|
|
DisposeProcess(Process);
|
|
|
|
Process.Exited -= Process_Exited;
|
|
|
|
|
|
|
|
DisposeProcess(Process);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_disposed = true;
|
|
|
|
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
_disposed = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|