display additional transcoding info in dashboard

This commit is contained in:
Luke Pulverenti
2014-06-05 20:39:02 -04:00
parent 7049ad66f4
commit f7cd7182d5
15 changed files with 260 additions and 74 deletions

View File

@@ -126,7 +126,7 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="state">The state.</param>
/// <returns>System.String.</returns>
protected string GetOutputFilePath(StreamState state)
private string GetOutputFilePath(StreamState state)
{
var folder = ServerConfigurationManager.ApplicationPaths.TranscodingTempPath;
@@ -726,12 +726,13 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="request">The request.</param>
/// <param name="audioStream">The audio stream.</param>
/// <param name="outputAudioCodec">The output audio codec.</param>
/// <returns>System.Nullable{System.Int32}.</returns>
private int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream)
private int? GetNumAudioChannelsParam(StreamRequest request, MediaStream audioStream, string outputAudioCodec)
{
if (audioStream != null)
{
var codec = request.AudioCodec ?? string.Empty;
var codec = outputAudioCodec ?? string.Empty;
if (audioStream.Channels > 2 && codec.IndexOf("wma", StringComparison.OrdinalIgnoreCase) != -1)
{
@@ -769,7 +770,7 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.String.</returns>
protected string GetAudioCodec(StreamRequest request)
private string GetAudioCodec(StreamRequest request)
{
var codec = request.AudioCodec;
@@ -798,7 +799,7 @@ namespace MediaBrowser.Api.Playback
/// </summary>
/// <param name="request">The request.</param>
/// <returns>System.String.</returns>
protected string GetVideoCodec(VideoStreamRequest request)
private string GetVideoCodec(VideoStreamRequest request)
{
var codec = request.VideoCodec;
@@ -866,7 +867,7 @@ namespace MediaBrowser.Api.Playback
Directory.CreateDirectory(Path.GetDirectoryName(outputPath));
if (state.IsInputVideo && state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
if (state.VideoType == VideoType.Iso && state.IsoType.HasValue && IsoManager.CanMount(state.MediaPath))
{
state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationTokenSource.Token).ConfigureAwait(false);
}
@@ -900,7 +901,13 @@ namespace MediaBrowser.Api.Playback
EnableRaisingEvents = true
};
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, TranscodingJobType, process, state.Request.StartTimeTicks, state.MediaPath, state.Request.DeviceId, cancellationTokenSource);
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
TranscodingJobType,
process,
state.Request.StartTimeTicks,
state.Request.DeviceId,
state,
cancellationTokenSource);
var commandLineLogMessage = process.StartInfo.FileName + " " + process.StartInfo.Arguments;
Logger.Info(commandLineLogMessage);
@@ -924,7 +931,7 @@ namespace MediaBrowser.Api.Playback
{
Logger.ErrorException("Error starting ffmpeg", ex);
ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType);
ApiEntryPoint.Instance.OnTranscodeFailedToStart(outputPath, TranscodingJobType, state);
throw;
}
@@ -932,10 +939,8 @@ namespace MediaBrowser.Api.Playback
// 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(state.LogFileStream);
#pragma warning restore 4014
StartStreamingLog(state, process.StandardError.BaseStream, state.LogFileStream);
// Wait for the file to exist before proceeeding
while (!File.Exists(outputPath))
@@ -956,6 +961,82 @@ namespace MediaBrowser.Api.Playback
}
}
private async void StartStreamingLog(StreamState state, Stream source, Stream target)
{
try
{
using (var reader = new StreamReader(source))
{
while (!reader.EndOfStream)
{
var line = await reader.ReadLineAsync().ConfigureAwait(false);
ParseLogLine(line, state);
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
await target.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false);
}
}
}
catch (Exception ex)
{
Logger.ErrorException("Error reading ffmpeg log", ex);
}
}
private void ParseLogLine(string line, StreamState state)
{
float? framerate = null;
double? percent = null;
var parts = line.Split(' ');
var totalMs = state.RunTimeTicks.HasValue
? TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalMilliseconds
: 0;
var startMs = state.Request.StartTimeTicks.HasValue
? TimeSpan.FromTicks(state.Request.StartTimeTicks.Value).TotalMilliseconds
: 0;
for (var i = 0; i < parts.Length; i++)
{
var part = parts[i];
if (string.Equals(part, "fps=", StringComparison.OrdinalIgnoreCase) &&
(i + 1 < parts.Length))
{
var rate = parts[i + 1];
float val;
if (float.TryParse(rate, NumberStyles.Any, UsCulture, out val))
{
framerate = val;
}
}
else if (state.RunTimeTicks.HasValue &&
part.StartsWith("time=", StringComparison.OrdinalIgnoreCase))
{
var time = part.Split(new[] { '=' }, 2).Last();
TimeSpan val;
if (TimeSpan.TryParse(time, UsCulture, out val))
{
var currentMs = startMs + val.TotalMilliseconds;
var percentVal = currentMs / totalMs;
percent = 100 * percentVal;
}
}
}
if (framerate.HasValue || percent.HasValue)
{
ApiEntryPoint.Instance.ReportTranscodingProgress(state, framerate, percent);
}
}
private int? GetVideoBitrateParamValue(VideoStreamRequest request, MediaStream videoStream)
{
var bitrate = request.VideoBitRate;
@@ -1500,23 +1581,27 @@ namespace MediaBrowser.Api.Playback
state.OutputAudioBitrate = GetAudioBitrateParam(state.Request, state.AudioStream);
state.OutputAudioSampleRate = request.AudioSampleRate;
state.OutputAudioChannels = GetNumAudioChannelsParam(state.Request, state.AudioStream);
state.OutputAudioCodec = GetAudioCodec(state.Request);
if (videoRequest != null)
{
state.OutputVideoCodec = GetVideoCodec(videoRequest);
state.OutputVideoBitrate = GetVideoBitrateParamValue(state.VideoRequest, state.VideoStream);
if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
{
videoRequest.VideoCodec = "copy";
state.OutputVideoCodec = "copy";
}
if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs))
{
request.AudioCodec = "copy";
state.OutputAudioCodec = "copy";
}
}
state.OutputFilePath = GetOutputFilePath(state);
return state;
}
@@ -1729,14 +1814,14 @@ namespace MediaBrowser.Api.Playback
return;
}
var audioCodec = state.Request.AudioCodec;
var audioCodec = state.OutputAudioCodec;
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
var videoCodec = state.OutputVideoCodec;
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
{
@@ -1807,7 +1892,12 @@ namespace MediaBrowser.Api.Playback
profile = DlnaManager.GetDefaultProfile();
}
var audioCodec = state.Request.AudioCodec;
var audioCodec = state.OutputAudioCodec;
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
if (state.VideoRequest == null)
{
@@ -1825,12 +1915,7 @@ namespace MediaBrowser.Api.Playback
}
else
{
if (string.Equals(audioCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.AudioStream != null)
{
audioCodec = state.AudioStream.Codec;
}
var videoCodec = state.VideoRequest == null ? null : state.VideoRequest.VideoCodec;
var videoCodec = state.OutputVideoCodec;
if (string.Equals(videoCodec, "copy", StringComparison.OrdinalIgnoreCase) && state.VideoStream != null)
{

View File

@@ -86,7 +86,7 @@ namespace MediaBrowser.Api.Playback.Hls
var state = GetState(request, cancellationTokenSource.Token).Result;
var playlist = GetOutputFilePath(state);
var playlist = state.OutputFilePath;
if (File.Exists(playlist))
{
@@ -307,7 +307,7 @@ namespace MediaBrowser.Api.Playback.Hls
if (hlsVideoRequest != null)
{
if (hlsVideoRequest.AppendBaselineStream && state.IsInputVideo)
if (hlsVideoRequest.AppendBaselineStream)
{
var lowBitratePath = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath) + "-low.m3u8");

View File

@@ -89,7 +89,7 @@ namespace MediaBrowser.Api.Playback.Hls
var state = await GetState(request, CancellationToken.None).ConfigureAwait(false);
var playlistPath = Path.ChangeExtension(GetOutputFilePath(state), ".m3u8");
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
var path = GetSegmentPath(playlistPath, index);
@@ -231,7 +231,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override string GetAudioArguments(StreamState state)
{
var codec = GetAudioCodec(state.Request);
var codec = state.OutputAudioCodec;
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{
@@ -266,7 +266,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override string GetVideoArguments(StreamState state, bool performSubtitleConversion)
{
var codec = GetVideoCodec(state.VideoRequest);
var codec = state.OutputVideoCodec;
// See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))

View File

@@ -118,7 +118,7 @@ namespace MediaBrowser.Api.Playback.Hls
/// <returns>System.String.</returns>
protected override string GetAudioArguments(StreamState state)
{
var codec = GetAudioCodec(state.Request);
var codec = state.OutputAudioCodec;
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{
@@ -160,7 +160,7 @@ namespace MediaBrowser.Api.Playback.Hls
protected override string GetVideoArguments(StreamState state,
bool performSubtitleConversion)
{
var codec = GetVideoCodec(state.VideoRequest);
var codec = state.OutputVideoCodec;
// See if we can save come cpu cycles by avoiding encoding
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))

View File

@@ -78,8 +78,6 @@ namespace MediaBrowser.Api.Playback.Progressive
/// <exception cref="System.InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
{
var request = state.Request;
var audioTranscodeParams = new List<string>();
var bitrate = state.OutputAudioBitrate;

View File

@@ -45,53 +45,51 @@ namespace MediaBrowser.Api.Playback.Progressive
return ext;
}
var videoRequest = state.Request as VideoStreamRequest;
var isVideoRequest = state.VideoRequest != null;
// Try to infer based on the desired video codec
if (videoRequest != null && !string.IsNullOrEmpty(videoRequest.VideoCodec))
if (isVideoRequest)
{
if (state.IsInputVideo)
{
if (string.Equals(videoRequest.VideoCodec, "h264", StringComparison.OrdinalIgnoreCase))
var videoCodec = state.VideoRequest.VideoCodec;
if (string.Equals(videoCodec, "h264", StringComparison.OrdinalIgnoreCase))
{
return ".ts";
}
if (string.Equals(videoRequest.VideoCodec, "theora", StringComparison.OrdinalIgnoreCase))
if (string.Equals(videoCodec, "theora", StringComparison.OrdinalIgnoreCase))
{
return ".ogv";
}
if (string.Equals(videoRequest.VideoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
if (string.Equals(videoCodec, "vpx", StringComparison.OrdinalIgnoreCase))
{
return ".webm";
}
if (string.Equals(videoRequest.VideoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
if (string.Equals(videoCodec, "wmv", StringComparison.OrdinalIgnoreCase))
{
return ".asf";
}
}
}
// Try to infer based on the desired audio codec
if (!string.IsNullOrEmpty(state.Request.AudioCodec))
if (!isVideoRequest)
{
if (!state.IsInputVideo)
var audioCodec = state.Request.AudioCodec;
if (string.Equals("aac", audioCodec, StringComparison.OrdinalIgnoreCase))
{
if (string.Equals("aac", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".aac";
}
if (string.Equals("mp3", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".mp3";
}
if (string.Equals("vorbis", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".ogg";
}
if (string.Equals("wma", state.Request.AudioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".wma";
}
return ".aac";
}
if (string.Equals("mp3", audioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".mp3";
}
if (string.Equals("vorbis", audioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".ogg";
}
if (string.Equals("wma", audioCodec, StringComparison.OrdinalIgnoreCase))
{
return ".wma";
}
}
@@ -134,7 +132,7 @@ namespace MediaBrowser.Api.Playback.Progressive
}
}
var outputPath = GetOutputFilePath(state);
var outputPath = state.OutputFilePath;
var outputPathExists = File.Exists(outputPath);
var isStatic = request.Static ||
@@ -243,7 +241,7 @@ namespace MediaBrowser.Api.Playback.Progressive
private async Task<object> GetStreamResult(StreamState state, IDictionary<string, string> responseHeaders, bool isHeadRequest)
{
// Use the command line args with a dummy playlist path
var outputPath = GetOutputFilePath(state);
var outputPath = state.OutputFilePath;
responseHeaders["Accept-Ranges"] = "none";

View File

@@ -95,7 +95,7 @@ namespace MediaBrowser.Api.Playback.Progressive
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool performSubtitleConversions)
{
// Get the output codec name
var videoCodec = GetVideoCodec(state.VideoRequest);
var videoCodec = state.OutputVideoCodec;
var format = string.Empty;
var keyFrame = string.Empty;
@@ -190,10 +190,8 @@ namespace MediaBrowser.Api.Playback.Progressive
return string.Empty;
}
var request = state.Request;
// Get the output codec name
var codec = GetAudioCodec(request);
var codec = state.OutputAudioCodec;
if (codec.Equals("copy", StringComparison.OrdinalIgnoreCase))
{

View File

@@ -163,6 +163,9 @@ namespace MediaBrowser.Api.Playback
}
}
public string OutputFilePath { get; set; }
public string OutputVideoCodec { get; set; }
public string OutputAudioCodec { get; set; }
public int? OutputAudioChannels;
public int? OutputAudioSampleRate;
public int? OutputAudioBitrate;