aboutsummaryrefslogtreecommitdiff
path: root/MediaBrowser.Api
diff options
context:
space:
mode:
Diffstat (limited to 'MediaBrowser.Api')
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs42
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj1
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs56
-rw-r--r--MediaBrowser.Api/Playback/EndlessStreamCopy.cs32
-rw-r--r--MediaBrowser.Api/Playback/StreamState.cs7
5 files changed, 120 insertions, 18 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index 785cc395c..d37e7f724 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -349,26 +349,42 @@ namespace MediaBrowser.Api
// Also don't cache video
if (!hasExitedSuccessfully || job.StartTimeTicks.HasValue || job.IsVideo)
{
- Logger.Info("Deleting partial stream file(s) {0}", job.Path);
+ DeletePartialStreamFiles(job.Path, job.Type, 0, 1500);
+ }
+ }
- await Task.Delay(1500).ConfigureAwait(false);
+ private async void DeletePartialStreamFiles(string path, TranscodingJobType jobType, int retryCount, int delayMs)
+ {
+ if (retryCount >= 5)
+ {
+ return;
+ }
- try
+ Logger.Info("Deleting partial stream file(s) {0}", path);
+
+ await Task.Delay(delayMs).ConfigureAwait(false);
+
+ try
+ {
+ if (jobType == TranscodingJobType.Progressive)
{
- if (job.Type == TranscodingJobType.Progressive)
- {
- DeleteProgressivePartialStreamFiles(job.Path);
- }
- else
- {
- DeleteHlsPartialStreamFiles(job.Path);
- }
+ DeleteProgressivePartialStreamFiles(path);
}
- catch (IOException ex)
+ else
{
- Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, job.Path);
+ DeleteHlsPartialStreamFiles(path);
}
}
+ catch (IOException ex)
+ {
+ Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, path);
+
+ DeletePartialStreamFiles(path, jobType, retryCount + 1, 500);
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error deleting partial stream file(s) {0}", ex, path);
+ }
}
/// <summary>
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index 0732ee00c..4fc989fc5 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -97,6 +97,7 @@
<Compile Include="NotificationsService.cs" />
<Compile Include="PackageReviewService.cs" />
<Compile Include="PackageService.cs" />
+ <Compile Include="Playback\EndlessStreamCopy.cs" />
<Compile Include="Playback\Hls\AudioHlsService.cs" />
<Compile Include="Playback\Hls\BaseHlsService.cs" />
<Compile Include="Playback\Hls\HlsSegmentResponseFilter.cs" />
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 450c0c681..1ce7d2db3 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -13,6 +13,7 @@ using MediaBrowser.Model.Drawing;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
+using MediaBrowser.Model.LiveTv;
using System;
using System.Collections.Generic;
using System.Diagnostics;
@@ -539,8 +540,8 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
protected string GetProbeSizeArgument(string mediaPath, bool isVideo, VideoType? videoType, IsoType? isoType)
{
- var type = !isVideo ? MediaEncoderHelpers.GetInputType(mediaPath, null, null) :
- MediaEncoderHelpers.GetInputType(mediaPath, videoType, isoType);
+ var type = !isVideo ? MediaEncoderHelpers.GetInputType(null, null) :
+ MediaEncoderHelpers.GetInputType(videoType, isoType);
return MediaEncoder.GetProbeSizeArgument(type);
}
@@ -654,6 +655,11 @@ namespace MediaBrowser.Api.Playback
/// <returns>System.String.</returns>
protected string GetInputArgument(StreamState state)
{
+ if (state.SendInputOverStandardInput)
+ {
+ return "-";
+ }
+
var type = InputType.AudioFile;
var inputPath = new[] { state.MediaPath };
@@ -705,7 +711,9 @@ namespace MediaBrowser.Api.Playback
Arguments = GetCommandLineArguments(outputPath, state, true),
WindowStyle = ProcessWindowStyle.Hidden,
- ErrorDialog = false
+ ErrorDialog = false,
+
+ RedirectStandardInput = state.SendInputOverStandardInput
},
EnableRaisingEvents = true
@@ -738,6 +746,11 @@ namespace MediaBrowser.Api.Playback
throw;
}
+ if (state.SendInputOverStandardInput)
+ {
+ StreamToStandardInput(process, state);
+ }
+
// MUST read both stdout and stderr asynchronously or a deadlock may occurr
process.BeginOutputReadLine();
@@ -763,6 +776,34 @@ namespace MediaBrowser.Api.Playback
}
}
+ private async void StreamToStandardInput(Process process, StreamState state)
+ {
+ state.StandardInputCancellationTokenSource = new CancellationTokenSource();
+
+ try
+ {
+ await StreamToStandardInputInternal(process, state).ConfigureAwait(false);
+ }
+ catch (OperationCanceledException)
+ {
+ Logger.Debug("Stream to standard input closed normally.");
+ }
+ catch (Exception ex)
+ {
+ Logger.ErrorException("Error writing to standard input", ex);
+ }
+ }
+
+ private async Task StreamToStandardInputInternal(Process process, StreamState state)
+ {
+ state.StandardInputCancellationTokenSource = new CancellationTokenSource();
+
+ using (var fileStream = FileSystem.GetFileStream(state.MediaPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite, true))
+ {
+ await new EndlessStreamCopy().CopyStream(fileStream, process.StandardInput.BaseStream, state.StandardInputCancellationTokenSource.Token).ConfigureAwait(false);
+ }
+ }
+
protected int? GetVideoBitrateParam(StreamState state)
{
return state.VideoRequest.VideoBitRate;
@@ -831,6 +872,11 @@ namespace MediaBrowser.Api.Playback
state.IsoMount = null;
}
+ if (state.StandardInputCancellationTokenSource != null)
+ {
+ state.StandardInputCancellationTokenSource.Cancel();
+ }
+
var outputFilePath = GetOutputFilePath(state);
state.LogFileStream.Dispose();
@@ -903,10 +949,11 @@ namespace MediaBrowser.Api.Playback
}
itemId = recording.Id;
+ state.SendInputOverStandardInput = recording.RecordingInfo.Status == RecordingStatus.InProgress;
}
else if (string.Equals(request.Type, "Channel", StringComparison.OrdinalIgnoreCase))
{
- var channel = LiveTvManager.GetInternalChannel(request.Id);
+ var channel = LiveTvManager.GetInternalChannel(request.Id);
state.VideoType = VideoType.VideoFile;
state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
@@ -926,6 +973,7 @@ namespace MediaBrowser.Api.Playback
}
itemId = channel.Id;
+ state.SendInputOverStandardInput = true;
}
else
{
diff --git a/MediaBrowser.Api/Playback/EndlessStreamCopy.cs b/MediaBrowser.Api/Playback/EndlessStreamCopy.cs
new file mode 100644
index 000000000..40586261f
--- /dev/null
+++ b/MediaBrowser.Api/Playback/EndlessStreamCopy.cs
@@ -0,0 +1,32 @@
+using System.IO;
+using System.Threading;
+using System.Threading.Tasks;
+
+namespace MediaBrowser.Api.Playback
+{
+ public class EndlessStreamCopy
+ {
+ public async Task CopyStream(Stream source, Stream target, CancellationToken cancellationToken)
+ {
+ long position = 0;
+
+ while (!cancellationToken.IsCancellationRequested)
+ {
+ await source.CopyToAsync(target, 81920, cancellationToken).ConfigureAwait(false);
+
+ var fsPosition = source.Position;
+
+ var bytesRead = fsPosition - position;
+
+ //Logger.Debug("Streamed {0} bytes from file {1}", bytesRead, path);
+
+ if (bytesRead == 0)
+ {
+ await Task.Delay(100, cancellationToken).ConfigureAwait(false);
+ }
+
+ position = fsPosition;
+ }
+ }
+ }
+}
diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs
index be1ad85eb..f705b5e33 100644
--- a/MediaBrowser.Api/Playback/StreamState.cs
+++ b/MediaBrowser.Api/Playback/StreamState.cs
@@ -1,4 +1,5 @@
-using MediaBrowser.Model.Entities;
+using System.Threading;
+using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO;
using System.Collections.Generic;
using System.IO;
@@ -47,5 +48,9 @@ namespace MediaBrowser.Api.Playback
public List<string> PlayableStreamFileNames { get; set; }
public bool HasMediaStreams { get; set; }
+
+ public bool SendInputOverStandardInput { get; set; }
+
+ public CancellationTokenSource StandardInputCancellationTokenSource { get; set; }
}
}