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