浏览代码

update recording encoder

Luke Pulverenti 8 年之前
父节点
当前提交
c30b82ab44

+ 5 - 0
Emby.Common.Implementations/EnvironmentInfo/EnvironmentInfo.cs

@@ -110,5 +110,10 @@ namespace Emby.Common.Implementations.EnvironmentInfo
         {
         {
             get { return Environment.StackTrace; }
             get { return Environment.StackTrace; }
         }
         }
+
+        public void SetProcessEnvironmentVariable(string name, string value)
+        {
+            Environment.SetEnvironmentVariable(name, value);
+        }
     }
     }
 }
 }

+ 2 - 1
Emby.Server.Core/ApplicationHost.cs

@@ -789,7 +789,8 @@ namespace Emby.Server.Core
                 MemoryStreamFactory,
                 MemoryStreamFactory,
                 ProcessFactory,
                 ProcessFactory,
                 (Environment.ProcessorCount > 2 ? 14000 : 40000),
                 (Environment.ProcessorCount > 2 ? 14000 : 40000),
-                EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows);
+                EnvironmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows,
+                EnvironmentInfo);
 
 
             MediaEncoder = mediaEncoder;
             MediaEncoder = mediaEncoder;
             RegisterSingleInstance(MediaEncoder);
             RegisterSingleInstance(MediaEncoder);

+ 18 - 65
Emby.Server.Implementations/LiveTv/EmbyTV/EncodedRecorder.cs

@@ -31,7 +31,6 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         private readonly IServerApplicationPaths _appPaths;
         private readonly IServerApplicationPaths _appPaths;
         private readonly LiveTvOptions _liveTvOptions;
         private readonly LiveTvOptions _liveTvOptions;
         private bool _hasExited;
         private bool _hasExited;
-        private Stream _logFileStream;
         private string _targetPath;
         private string _targetPath;
         private IProcess _process;
         private IProcess _process;
         private readonly IProcessFactory _processFactory;
         private readonly IProcessFactory _processFactory;
@@ -85,22 +84,26 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             _targetPath = targetFile;
             _targetPath = targetFile;
             _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
             _fileSystem.CreateDirectory(Path.GetDirectoryName(targetFile));
 
 
+            var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid().ToString("N") + ".txt");
+            _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
+
             var process = _processFactory.Create(new ProcessOptions
             var process = _processFactory.Create(new ProcessOptions
             {
             {
                 CreateNoWindow = true,
                 CreateNoWindow = true,
-                UseShellExecute = false,
+                UseShellExecute = true,
 
 
                 // Must consume both stdout and stderr or deadlocks may occur
                 // Must consume both stdout and stderr or deadlocks may occur
                 //RedirectStandardOutput = true,
                 //RedirectStandardOutput = true,
-                RedirectStandardError = true,
-                RedirectStandardInput = true,
+                RedirectStandardError = false,
+                RedirectStandardInput = false,
 
 
                 FileName = _mediaEncoder.EncoderPath,
                 FileName = _mediaEncoder.EncoderPath,
                 Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
                 Arguments = GetCommandLineArgs(mediaSource, inputFile, targetFile, duration),
 
 
                 IsHidden = true,
                 IsHidden = true,
                 ErrorDialog = false,
                 ErrorDialog = false,
-                EnableRaisingEvents = true
+                EnableRaisingEvents = true,
+                WorkingDirectory = Path.GetDirectoryName(logFilePath)
             });
             });
 
 
             _process = process;
             _process = process;
@@ -108,14 +111,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
             var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
             _logger.Info(commandLineLogMessage);
             _logger.Info(commandLineLogMessage);
 
 
-            var logFilePath = Path.Combine(_appPaths.LogDirectoryPath, "record-transcode-" + Guid.NewGuid() + ".txt");
-            _fileSystem.CreateDirectory(Path.GetDirectoryName(logFilePath));
+            _mediaEncoder.SetLogFilename(Path.GetFileName(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, FileOpenMode.Create, FileAccessMode.Write, FileShareMode.Read, true);
-
-            var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
-            _logFileStream.Write(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length);
+            //var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
 
 
             process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
             process.Exited += (sender, args) => OnFfMpegProcessExited(process, inputFile);
 
 
@@ -128,10 +126,8 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
             onStarted();
             onStarted();
 
 
-            // Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
-            StartStreamingLog(process.StandardError.BaseStream, _logFileStream);
-
             _logger.Info("ffmpeg recording process started for {0}", _targetPath);
             _logger.Info("ffmpeg recording process started for {0}", _targetPath);
+            _mediaEncoder.ClearLogFilename();
 
 
             return _taskCompletionSource.Task;
             return _taskCompletionSource.Task;
         }
         }
@@ -154,7 +150,7 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
 
 
             var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
             var durationParam = " -t " + _mediaEncoder.GetTimeParameter(duration.Ticks);
             var inputModifiers = "-fflags +genpts -async 1 -vsync -1";
             var inputModifiers = "-fflags +genpts -async 1 -vsync -1";
-            var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -y \"{1}\"";
+            var commandLineArgs = "-i \"{0}\"{4} -sn {2} -map_metadata -1 -threads 0 {3} -loglevel info -y \"{1}\"";
 
 
             long startTimeTicks = 0;
             long startTimeTicks = 0;
             //if (mediaSource.DateLiveStreamOpened.HasValue)
             //if (mediaSource.DateLiveStreamOpened.HasValue)
@@ -234,16 +230,19 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
             return output;
             return output;
         }
         }
 
 
+        private bool _isCancelled;
         private void Stop()
         private void Stop()
         {
         {
             if (!_hasExited)
             if (!_hasExited)
             {
             {
                 try
                 try
                 {
                 {
+                    _isCancelled = true;
+
                     _logger.Info("Killing ffmpeg recording process for {0}", _targetPath);
                     _logger.Info("Killing ffmpeg recording process for {0}", _targetPath);
 
 
-                    //process.Kill();
-                    _process.StandardInput.WriteLine("q");
+                    _process.Kill();
+                    //_process.StandardInput.WriteLine("q");
                 }
                 }
                 catch (Exception ex)
                 catch (Exception ex)
                 {
                 {
@@ -259,11 +258,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
         {
         {
             _hasExited = true;
             _hasExited = true;
 
 
-            DisposeLogStream();
-
             try
             try
             {
             {
-                var exitCode = process.ExitCode;
+                var exitCode = _isCancelled ? 0 : process.ExitCode;
 
 
                 _logger.Info("FFMpeg recording exited with code {0} for {1}", exitCode, _targetPath);
                 _logger.Info("FFMpeg recording exited with code {0} for {1}", exitCode, _targetPath);
 
 
@@ -282,49 +279,5 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV
                 _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
                 _taskCompletionSource.TrySetException(new Exception(string.Format("Recording for {0} failed", _targetPath)));
             }
             }
         }
         }
-
-        private void DisposeLogStream()
-        {
-            if (_logFileStream != null)
-            {
-                try
-                {
-                    _logFileStream.Dispose();
-                }
-                catch (Exception ex)
-                {
-                    _logger.ErrorException("Error disposing recording log stream", ex);
-                }
-
-                _logFileStream = null;
-            }
-        }
-
-        private async void StartStreamingLog(Stream source, Stream target)
-        {
-            try
-            {
-                using (var reader = new StreamReader(source))
-                {
-                    while (!reader.EndOfStream)
-                    {
-                        var line = await reader.ReadLineAsync().ConfigureAwait(false);
-
-                        var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
-
-                        await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
-                        await target.FlushAsync().ConfigureAwait(false);
-                    }
-                }
-            }
-            catch (ObjectDisposedException)
-            {
-                // Don't spam the log. This doesn't seem to throw in windows, but sometimes under linux
-            }
-            catch (Exception ex)
-            {
-                _logger.ErrorException("Error reading ffmpeg recording log", ex);
-            }
-        }
     }
     }
 }
 }

+ 3 - 0
MediaBrowser.Controller/MediaEncoding/IMediaEncoder.cs

@@ -135,5 +135,8 @@ namespace MediaBrowser.Controller.MediaEncoding
         Task UpdateEncoderPath(string path, string pathType);
         Task UpdateEncoderPath(string path, string pathType);
         bool SupportsEncoder(string encoder);
         bool SupportsEncoder(string encoder);
         bool IsDefaultEncoderPath { get; }
         bool IsDefaultEncoderPath { get; }
+
+        void SetLogFilename(string name);
+        void ClearLogFilename();
     }
     }
 }
 }

+ 58 - 1
MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs

@@ -24,6 +24,7 @@ using MediaBrowser.Common.Configuration;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Extensions;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Model.Diagnostics;
 using MediaBrowser.Model.Diagnostics;
+using MediaBrowser.Model.System;
 
 
 namespace MediaBrowser.MediaEncoding.Encoder
 namespace MediaBrowser.MediaEncoding.Encoder
 {
 {
@@ -88,9 +89,11 @@ namespace MediaBrowser.MediaEncoding.Encoder
         private readonly int DefaultImageExtractionTimeoutMs;
         private readonly int DefaultImageExtractionTimeoutMs;
         private readonly bool EnableEncoderFontFile;
         private readonly bool EnableEncoderFontFile;
 
 
+        private readonly IEnvironmentInfo _environmentInfo;
+
         public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory,
         public MediaEncoder(ILogger logger, IJsonSerializer jsonSerializer, string ffMpegPath, string ffProbePath, bool hasExternalEncoder, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, Func<ISubtitleEncoder> subtitleEncoder, Func<IMediaSourceManager> mediaSourceManager, IHttpClient httpClient, IZipClient zipClient, IMemoryStreamFactory memoryStreamProvider, IProcessFactory processFactory,
             int defaultImageExtractionTimeoutMs,
             int defaultImageExtractionTimeoutMs,
-            bool enableEncoderFontFile)
+            bool enableEncoderFontFile, IEnvironmentInfo environmentInfo)
         {
         {
             _logger = logger;
             _logger = logger;
             _jsonSerializer = jsonSerializer;
             _jsonSerializer = jsonSerializer;
@@ -109,12 +112,66 @@ namespace MediaBrowser.MediaEncoding.Encoder
             _processFactory = processFactory;
             _processFactory = processFactory;
             DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
             DefaultImageExtractionTimeoutMs = defaultImageExtractionTimeoutMs;
             EnableEncoderFontFile = enableEncoderFontFile;
             EnableEncoderFontFile = enableEncoderFontFile;
+            _environmentInfo = environmentInfo;
             FFProbePath = ffProbePath;
             FFProbePath = ffProbePath;
             FFMpegPath = ffMpegPath;
             FFMpegPath = ffMpegPath;
             _originalFFProbePath = ffProbePath;
             _originalFFProbePath = ffProbePath;
             _originalFFMpegPath = ffMpegPath;
             _originalFFMpegPath = ffMpegPath;
 
 
             _hasExternalEncoder = hasExternalEncoder;
             _hasExternalEncoder = hasExternalEncoder;
+
+            SetEnvironmentVariable();
+        }
+
+        private readonly object _logLock = new object();
+        public void SetLogFilename(string name)
+        {
+            lock (_logLock)
+            {
+                try
+                {
+                    _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=" + name + ":level=32");
+                }
+                catch (Exception ex)
+                {
+                    _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+                }
+            }
+        }
+
+        public void ClearLogFilename()
+        {
+            lock (_logLock)
+            {
+                try
+                {
+                    _environmentInfo.SetProcessEnvironmentVariable("FFREPORT", null);
+                }
+                catch (Exception ex)
+                {
+                    //_logger.ErrorException("Error setting FFREPORT environment variable", ex);
+                }
+            }
+        }
+
+        private void SetEnvironmentVariable()
+        {
+            try
+            {
+                //_environmentInfo.SetProcessEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+            }
+            try
+            {
+                //_environmentInfo.SetUserEnvironmentVariable("FFREPORT", "file=program-YYYYMMDD-HHMMSS.txt:level=32");
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error setting FFREPORT environment variable", ex);
+            }
         }
         }
 
 
         public string EncoderLocationType
         public string EncoderLocationType

+ 2 - 6
MediaBrowser.Model/System/IEnvironmentInfo.cs

@@ -1,9 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Text;
-using System.Threading.Tasks;
-
+
 namespace MediaBrowser.Model.System
 namespace MediaBrowser.Model.System
 {
 {
     public interface IEnvironmentInfo
     public interface IEnvironmentInfo
@@ -13,6 +8,7 @@ namespace MediaBrowser.Model.System
         string OperatingSystemVersion { get; }
         string OperatingSystemVersion { get; }
         Architecture SystemArchitecture { get; }
         Architecture SystemArchitecture { get; }
         string GetEnvironmentVariable(string name);
         string GetEnvironmentVariable(string name);
+        void SetProcessEnvironmentVariable(string name, string value);
         string GetUserId();
         string GetUserId();
         string StackTrace { get; }
         string StackTrace { get; }
     }
     }