update recording encoder

pull/702/head
Luke Pulverenti 8 years ago
parent c80ac9b823
commit c30b82ab44

@ -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);
}
} }
} }

@ -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);

@ -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, RedirectStandardError = false,
RedirectStandardInput = true, 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"); _mediaEncoder.SetLogFilename(Path.GetFileName(logFilePath));
_fileSystem.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. //var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(_json.SerializeToString(mediaSource) + Environment.NewLine + Environment.NewLine + commandLineLogMessage + Environment.NewLine + Environment.NewLine);
_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);
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.Kill();
_process.StandardInput.WriteLine("q"); //_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);
}
}
} }
} }

@ -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();
} }
} }

@ -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

@ -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; }
} }

Loading…
Cancel
Save