aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs')
-rw-r--r--MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs168
1 files changed, 168 insertions, 0 deletions
diff --git a/MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs b/MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs
new file mode 100644
index 000000000..05733aef0
--- /dev/null
+++ b/MediaBrowser.MediaEncoding/Encoder/FFMpegProcess.cs
@@ -0,0 +1,168 @@
+using MediaBrowser.Common.Configuration;
+using MediaBrowser.Common.IO;
+using MediaBrowser.Controller.LiveTv;
+using MediaBrowser.Model.Entities;
+using MediaBrowser.Model.IO;
+using MediaBrowser.Model.Logging;
+using System;
+using System.Diagnostics;
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.MediaEncoding.Encoder
+{
+ public class FFMpegProcess : IDisposable
+ {
+ private readonly string _ffmpegPath;
+ private readonly ILogger _logger;
+ private readonly IFileSystem _fileSystem;
+ private readonly IApplicationPaths _appPaths;
+ private readonly IIsoManager _isoManager;
+ private readonly ILiveTvManager _liveTvManager;
+
+ private Stream _logFileStream;
+ private InternalEncodingTask _task;
+ private IIsoMount _isoMount;
+
+ public FFMpegProcess(string ffmpegPath, ILogger logger, IFileSystem fileSystem, IApplicationPaths appPaths, IIsoManager isoManager, ILiveTvManager liveTvManager)
+ {
+ _ffmpegPath = ffmpegPath;
+ _logger = logger;
+ _fileSystem = fileSystem;
+ _appPaths = appPaths;
+ _isoManager = isoManager;
+ _liveTvManager = liveTvManager;
+ }
+
+ public async Task Start(InternalEncodingTask task, Func<InternalEncodingTask,string,string> argumentsFactory)
+ {
+ _task = task;
+ if (!File.Exists(_ffmpegPath))
+ {
+ throw new InvalidOperationException("ffmpeg was not found at " + _ffmpegPath);
+ }
+
+ Directory.CreateDirectory(Path.GetDirectoryName(task.Request.OutputPath));
+
+ string mountedPath = null;
+ if (task.InputVideoType.HasValue && task.InputVideoType == VideoType.Iso && task.IsoType.HasValue)
+ {
+ if (_isoManager.CanMount(task.MediaPath))
+ {
+ _isoMount = await _isoManager.Mount(task.MediaPath, CancellationToken.None).ConfigureAwait(false);
+ mountedPath = _isoMount.MountedPath;
+ }
+ }
+
+ var process = new Process
+ {
+ StartInfo = new ProcessStartInfo
+ {
+ CreateNoWindow = true,
+ UseShellExecute = false,
+
+ // Must consume both stdout and stderr or deadlocks may occur
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+
+ FileName = _ffmpegPath,
+ WorkingDirectory = Path.GetDirectoryName(_ffmpegPath),
+ Arguments = argumentsFactory(task, mountedPath),
+
+ WindowStyle = ProcessWindowStyle.Hidden,
+ ErrorDialog = false
+ },
+
+ EnableRaisingEvents = true
+ };
+
+ _logger.Info(process.StartInfo.FileName + " " + process.StartInfo.Arguments);
+
+ var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "ffmpeg-" + task.Id + ".txt");
+ Directory.CreateDirectory(Path.GetDirectoryName(logFilePath));
+
+ // FFMpeg writes debug/error info to stderr. This is useful when debugging so let's put it in the log directory.
+ _logFileStream = _fileSystem.GetFileStream(logFilePath, FileMode.Create, FileAccess.Write, FileShare.Read, true);
+
+ process.Exited += process_Exited;
+
+ try
+ {
+ process.Start();
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error starting ffmpeg", ex);
+
+ task.OnError();
+
+ DisposeLogFileStream();
+
+ process.Dispose();
+
+ throw;
+ }
+
+ task.OnBegin();
+
+ // MUST read both stdout and stderr asynchronously or a deadlock may occurr
+ process.BeginOutputReadLine();
+
+#pragma warning disable 4014
+ // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
+ process.StandardError.BaseStream.CopyToAsync(_logFileStream);
+#pragma warning restore 4014
+ }
+
+ async void process_Exited(object sender, EventArgs e)
+ {
+ var process = (Process)sender;
+
+ if (_isoMount != null)
+ {
+ _isoMount.Dispose();
+ _isoMount = null;
+ }
+
+ DisposeLogFileStream();
+
+ try
+ {
+ _logger.Info("FFMpeg exited with code {0} for {1}", process.ExitCode, _task.Request.OutputPath);
+ }
+ catch
+ {
+ _logger.Info("FFMpeg exited with an error for {0}", _task.Request.OutputPath);
+ }
+
+ _task.OnCompleted();
+
+ if (!string.IsNullOrEmpty(_task.LiveTvStreamId))
+ {
+ try
+ {
+ await _liveTvManager.CloseLiveStream(_task.LiveTvStreamId, CancellationToken.None).ConfigureAwait(false);
+ }
+ catch (Exception ex)
+ {
+ _logger.ErrorException("Error closing live tv stream", ex);
+ }
+ }
+ }
+
+ public void Dispose()
+ {
+ DisposeLogFileStream();
+ }
+
+ private void DisposeLogFileStream()
+ {
+ if (_logFileStream != null)
+ {
+ _logFileStream.Dispose();
+ _logFileStream = null;
+ }
+ }
+ }
+}