restore nuget targets for mono build
This commit is contained in:
parent
a3d553a7fb
commit
60d1d5cdee
|
@ -120,12 +120,15 @@ namespace MediaBrowser.Api
|
|||
/// Called when [transcode beginning].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="transcodingJobId">The transcoding job identifier.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
/// <param name="process">The process.</param>
|
||||
/// <param name="deviceId">The device id.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="cancellationTokenSource">The cancellation token source.</param>
|
||||
public void OnTranscodeBeginning(string path,
|
||||
/// <returns>TranscodingJob.</returns>
|
||||
public TranscodingJob OnTranscodeBeginning(string path,
|
||||
string transcodingJobId,
|
||||
TranscodingJobType type,
|
||||
Process process,
|
||||
string deviceId,
|
||||
|
@ -134,22 +137,37 @@ namespace MediaBrowser.Api
|
|||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
_activeTranscodingJobs.Add(new TranscodingJob
|
||||
var job = new TranscodingJob
|
||||
{
|
||||
Type = type,
|
||||
Path = path,
|
||||
Process = process,
|
||||
ActiveRequestCount = 1,
|
||||
DeviceId = deviceId,
|
||||
CancellationTokenSource = cancellationTokenSource
|
||||
});
|
||||
CancellationTokenSource = cancellationTokenSource,
|
||||
Id = transcodingJobId
|
||||
};
|
||||
|
||||
ReportTranscodingProgress(state, null, null);
|
||||
_activeTranscodingJobs.Add(job);
|
||||
|
||||
ReportTranscodingProgress(job, state, null, null, null, null);
|
||||
|
||||
return job;
|
||||
}
|
||||
}
|
||||
|
||||
public void ReportTranscodingProgress(StreamState state, float? framerate, double? percentComplete)
|
||||
public void ReportTranscodingProgress(TranscodingJob job, StreamState state, TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded)
|
||||
{
|
||||
var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null;
|
||||
|
||||
if (job != null)
|
||||
{
|
||||
job.Framerate = framerate;
|
||||
job.CompletionPercentage = percentComplete;
|
||||
job.TranscodingPositionTicks = ticks;
|
||||
job.BytesTranscoded = bytesTranscoded;
|
||||
}
|
||||
|
||||
var deviceId = state.Request.DeviceId;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(deviceId))
|
||||
|
@ -226,12 +244,20 @@ namespace MediaBrowser.Api
|
|||
}
|
||||
}
|
||||
|
||||
public TranscodingJob GetTranscodingJob(string id)
|
||||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
return _activeTranscodingJobs.FirstOrDefault(j => j.Id.Equals(id, StringComparison.OrdinalIgnoreCase));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode begin request].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public void OnTranscodeBeginRequest(string path, TranscodingJobType type)
|
||||
public TranscodingJob OnTranscodeBeginRequest(string path, TranscodingJobType type)
|
||||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
{
|
||||
|
@ -239,7 +265,7 @@ namespace MediaBrowser.Api
|
|||
|
||||
if (job == null)
|
||||
{
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
|
||||
job.ActiveRequestCount++;
|
||||
|
@ -249,40 +275,27 @@ namespace MediaBrowser.Api
|
|||
job.KillTimer.Dispose();
|
||||
job.KillTimer = null;
|
||||
}
|
||||
|
||||
return job;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [transcode end request].
|
||||
/// </summary>
|
||||
/// <param name="path">The path.</param>
|
||||
/// <param name="type">The type.</param>
|
||||
public void OnTranscodeEndRequest(string path, TranscodingJobType type)
|
||||
public void OnTranscodeEndRequest(TranscodingJob job)
|
||||
{
|
||||
lock (_activeTranscodingJobs)
|
||||
job.ActiveRequestCount--;
|
||||
|
||||
if (job.ActiveRequestCount == 0)
|
||||
{
|
||||
var job = _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
|
||||
// The HLS kill timer is long - 1/2 hr. clients should use the manual kill command when stopping.
|
||||
var timerDuration = job.Type == TranscodingJobType.Progressive ? 1000 : 1800000;
|
||||
|
||||
if (job == null)
|
||||
if (job.KillTimer == null)
|
||||
{
|
||||
return;
|
||||
job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite);
|
||||
}
|
||||
|
||||
job.ActiveRequestCount--;
|
||||
|
||||
if (job.ActiveRequestCount == 0)
|
||||
else
|
||||
{
|
||||
// The HLS kill timer is long - 1/2 hr. clients should use the manual kill command when stopping.
|
||||
var timerDuration = type == TranscodingJobType.Progressive ? 1000 : 1800000;
|
||||
|
||||
if (job.KillTimer == null)
|
||||
{
|
||||
job.KillTimer = new Timer(OnTranscodeKillTimerStopped, job, timerDuration, Timeout.Infinite);
|
||||
}
|
||||
else
|
||||
{
|
||||
job.KillTimer.Change(timerDuration, Timeout.Infinite);
|
||||
}
|
||||
job.KillTimer.Change(timerDuration, Timeout.Infinite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -306,7 +319,6 @@ namespace MediaBrowser.Api
|
|||
/// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="ArgumentNullException">deviceId</exception>
|
||||
/// <exception cref="System.ArgumentNullException">sourcePath</exception>
|
||||
internal Task KillTranscodingJobs(string deviceId, Func<string, bool> deleteFiles, bool acquireLock)
|
||||
{
|
||||
if (string.IsNullOrEmpty(deviceId))
|
||||
|
@ -324,8 +336,7 @@ namespace MediaBrowser.Api
|
|||
/// <param name="deleteFiles">The delete files.</param>
|
||||
/// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="System.ArgumentNullException">deviceId</exception>
|
||||
internal async Task KillTranscodingJobs(Func<TranscodingJob,bool> killJob, Func<string, bool> deleteFiles, bool acquireLock)
|
||||
internal async Task KillTranscodingJobs(Func<TranscodingJob, bool> killJob, Func<string, bool> deleteFiles, bool acquireLock)
|
||||
{
|
||||
var jobs = new List<TranscodingJob>();
|
||||
|
||||
|
@ -542,6 +553,17 @@ namespace MediaBrowser.Api
|
|||
public object ProcessLock = new object();
|
||||
|
||||
public bool HasExited { get; set; }
|
||||
|
||||
public string Id { get; set; }
|
||||
|
||||
public float? Framerate { get; set; }
|
||||
public double? CompletionPercentage { get; set; }
|
||||
|
||||
public long? BytesDownloaded { get; set; }
|
||||
public long? BytesTranscoded { get; set; }
|
||||
|
||||
public long? TranscodingPositionTicks { get; set; }
|
||||
public long? DownloadPositionTicks { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -169,7 +169,7 @@
|
|||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
|
@ -90,10 +90,11 @@ namespace MediaBrowser.Api.Playback
|
|||
/// Gets the command line arguments.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="transcodingJobId">The transcoding job identifier.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected abstract string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding);
|
||||
protected abstract string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding);
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the transcoding job.
|
||||
|
@ -122,7 +123,7 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
var outputFileExtension = GetOutputFileExtension(state);
|
||||
|
||||
var data = GetCommandLineArguments("dummy\\dummy", state, false);
|
||||
var data = GetCommandLineArguments("dummy\\dummy", "dummyTranscodingId", state, false);
|
||||
|
||||
data += "-" + (state.Request.DeviceId ?? string.Empty);
|
||||
|
||||
|
@ -782,9 +783,10 @@ namespace MediaBrowser.Api.Playback
|
|||
/// <summary>
|
||||
/// Gets the input argument.
|
||||
/// </summary>
|
||||
/// <param name="transcodingJobId">The transcoding job identifier.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected string GetInputArgument(StreamState state)
|
||||
protected string GetInputArgument(string transcodingJobId, StreamState state)
|
||||
{
|
||||
if (state.InputProtocol == MediaProtocol.File &&
|
||||
state.RunTimeTicks.HasValue &&
|
||||
|
@ -795,6 +797,8 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
var url = "http://localhost:" + ServerConfigurationManager.Configuration.HttpServerPortNumber.ToString(UsCulture) + "/mediabrowser/videos/" + state.Request.Id + "/stream?static=true&Throttle=true&mediaSourceId=" + state.Request.MediaSourceId;
|
||||
|
||||
url += "&transcodingJobId=" + transcodingJobId;
|
||||
|
||||
return string.Format("\"{0}\"", url);
|
||||
}
|
||||
}
|
||||
|
@ -897,7 +901,7 @@ namespace MediaBrowser.Api.Playback
|
|||
/// <param name="cancellationTokenSource">The cancellation token source.</param>
|
||||
/// <returns>Task.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">ffmpeg was not found at + MediaEncoder.EncoderPath</exception>
|
||||
protected async Task StartFfMpeg(StreamState state, string outputPath, CancellationTokenSource cancellationTokenSource)
|
||||
protected async Task<TranscodingJob> StartFfMpeg(StreamState state, string outputPath, CancellationTokenSource cancellationTokenSource)
|
||||
{
|
||||
if (!File.Exists(MediaEncoder.EncoderPath))
|
||||
{
|
||||
|
@ -908,7 +912,8 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false);
|
||||
|
||||
var commandLineArgs = GetCommandLineArguments(outputPath, state, true);
|
||||
var transcodingId = Guid.NewGuid().ToString("N");
|
||||
var commandLineArgs = GetCommandLineArguments(outputPath, transcodingId, state, true);
|
||||
|
||||
if (ServerConfigurationManager.Configuration.EnableDebugEncodingLogging)
|
||||
{
|
||||
|
@ -938,7 +943,8 @@ namespace MediaBrowser.Api.Playback
|
|||
EnableRaisingEvents = true
|
||||
};
|
||||
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
|
||||
var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
|
||||
transcodingId,
|
||||
TranscodingJobType,
|
||||
process,
|
||||
state.Request.DeviceId,
|
||||
|
@ -957,7 +963,7 @@ namespace MediaBrowser.Api.Playback
|
|||
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
|
||||
await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
|
||||
process.Exited += (sender, args) => OnFfMpegProcessExited(process, state, outputPath);
|
||||
process.Exited += (sender, args) => OnFfMpegProcessExited(process, transcodingJob, state);
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -976,16 +982,18 @@ namespace MediaBrowser.Api.Playback
|
|||
process.BeginOutputReadLine();
|
||||
|
||||
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback
|
||||
StartStreamingLog(state, process.StandardError.BaseStream, state.LogFileStream);
|
||||
StartStreamingLog(transcodingJob, state, process.StandardError.BaseStream, state.LogFileStream);
|
||||
|
||||
// Wait for the file to exist before proceeeding
|
||||
while (!File.Exists(outputPath))
|
||||
{
|
||||
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return transcodingJob;
|
||||
}
|
||||
|
||||
private async void StartStreamingLog(StreamState state, Stream source, Stream target)
|
||||
private async void StartStreamingLog(TranscodingJob transcodingJob, StreamState state, Stream source, Stream target)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -995,7 +1003,7 @@ namespace MediaBrowser.Api.Playback
|
|||
{
|
||||
var line = await reader.ReadLineAsync().ConfigureAwait(false);
|
||||
|
||||
ParseLogLine(line, state);
|
||||
ParseLogLine(line, transcodingJob, state);
|
||||
|
||||
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line);
|
||||
|
||||
|
@ -1009,10 +1017,12 @@ namespace MediaBrowser.Api.Playback
|
|||
}
|
||||
}
|
||||
|
||||
private void ParseLogLine(string line, StreamState state)
|
||||
private void ParseLogLine(string line, TranscodingJob transcodingJob, StreamState state)
|
||||
{
|
||||
float? framerate = null;
|
||||
double? percent = null;
|
||||
TimeSpan? transcodingPosition = null;
|
||||
long? bytesTranscoded = null;
|
||||
|
||||
var parts = line.Split(' ');
|
||||
|
||||
|
@ -1051,13 +1061,36 @@ namespace MediaBrowser.Api.Playback
|
|||
|
||||
var percentVal = currentMs / totalMs;
|
||||
percent = 100 * percentVal;
|
||||
|
||||
transcodingPosition = val;
|
||||
}
|
||||
}
|
||||
else if (part.StartsWith("size=", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var size = part.Split(new[] { '=' }, 2).Last();
|
||||
|
||||
int? scale = null;
|
||||
if (size.IndexOf("kb", StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
scale = 1024;
|
||||
size = size.Replace("kb", string.Empty, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
if (scale.HasValue)
|
||||
{
|
||||
long val;
|
||||
|
||||
if (long.TryParse(size, NumberStyles.Any, UsCulture, out val))
|
||||
{
|
||||
bytesTranscoded = val * scale.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (framerate.HasValue || percent.HasValue)
|
||||
{
|
||||
ApiEntryPoint.Instance.ReportTranscodingProgress(state, framerate, percent);
|
||||
ApiEntryPoint.Instance.ReportTranscodingProgress(transcodingJob, state, transcodingPosition, framerate, percent, bytesTranscoded);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1170,12 +1203,10 @@ namespace MediaBrowser.Api.Playback
|
|||
/// Processes the exited.
|
||||
/// </summary>
|
||||
/// <param name="process">The process.</param>
|
||||
/// <param name="job">The job.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
private void OnFfMpegProcessExited(Process process, StreamState state, string outputPath)
|
||||
private void OnFfMpegProcessExited(Process process, TranscodingJob job, StreamState state)
|
||||
{
|
||||
var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType);
|
||||
|
||||
if (job != null)
|
||||
{
|
||||
job.HasExited = true;
|
||||
|
|
|
@ -88,10 +88,11 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
}
|
||||
|
||||
var playlist = state.OutputFilePath;
|
||||
TranscodingJob job;
|
||||
|
||||
if (File.Exists(playlist))
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -100,14 +101,14 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
{
|
||||
if (File.Exists(playlist))
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the playlist doesn't already exist, startup ffmpeg
|
||||
try
|
||||
{
|
||||
await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
|
||||
job = await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -137,7 +138,10 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
}
|
||||
finally
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
|
||||
if (job != null)
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -162,7 +166,10 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
}
|
||||
finally
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
|
||||
if (job != null)
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -241,14 +248,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command line arguments.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
|
||||
{
|
||||
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream;
|
||||
|
||||
|
@ -276,7 +276,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
var args = string.Format("{0} {1} -i {2} -map_metadata -1 -threads {3} {4} {5} -sc_threshold 0 {6} -hls_time {7} -start_number {8} -hls_list_size {9}{10} -y \"{11}\"",
|
||||
itsOffset,
|
||||
inputModifier,
|
||||
GetInputArgument(state),
|
||||
GetInputArgument(transcodingJobId, state),
|
||||
threads,
|
||||
GetMapArgs(state),
|
||||
GetVideoArguments(state),
|
||||
|
|
|
@ -6,6 +6,7 @@ using MediaBrowser.Controller.Dlna;
|
|||
using MediaBrowser.Controller.Library;
|
||||
using MediaBrowser.Controller.LiveTv;
|
||||
using MediaBrowser.Controller.MediaEncoding;
|
||||
using MediaBrowser.Controller.Net;
|
||||
using MediaBrowser.Model.Dlna;
|
||||
using MediaBrowser.Model.Entities;
|
||||
using MediaBrowser.Model.IO;
|
||||
|
@ -107,11 +108,14 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
|
||||
|
||||
var segmentPath = GetSegmentPath(playlistPath, index);
|
||||
var segmentLength = state.SegmentLength;
|
||||
|
||||
TranscodingJob job = null;
|
||||
|
||||
if (File.Exists(segmentPath))
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
|
||||
return await GetSegmentResult(playlistPath, segmentPath, index, cancellationToken).ConfigureAwait(false);
|
||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
|
||||
return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
|
@ -119,8 +123,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
{
|
||||
if (File.Exists(segmentPath))
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
|
||||
return await GetSegmentResult(playlistPath, segmentPath, index, cancellationToken).ConfigureAwait(false);
|
||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
|
||||
return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -141,7 +145,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
var startSeconds = index * state.SegmentLength;
|
||||
request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks;
|
||||
|
||||
await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
|
||||
job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
@ -165,7 +169,8 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
}
|
||||
|
||||
Logger.Info("returning {0}", segmentPath);
|
||||
return await GetSegmentResult(playlistPath, segmentPath, index, cancellationToken).ConfigureAwait(false);
|
||||
job = job ?? ApiEntryPoint.Instance.GetTranscodingJob(playlistPath, TranscodingJobType.Hls);
|
||||
return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
public int? GetCurrentTranscodingIndex(string playlist)
|
||||
|
@ -258,12 +263,17 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
return Path.Combine(folder, filename + index.ToString(UsCulture) + ".ts");
|
||||
}
|
||||
|
||||
private async Task<object> GetSegmentResult(string playlistPath, string segmentPath, int segmentIndex, CancellationToken cancellationToken)
|
||||
private async Task<object> GetSegmentResult(string playlistPath,
|
||||
string segmentPath,
|
||||
int segmentIndex,
|
||||
int segmentLength,
|
||||
TranscodingJob transcodingJob,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
// If all transcoding has completed, just return immediately
|
||||
if (!IsTranscoding(playlistPath))
|
||||
{
|
||||
return ResultFactory.GetStaticFileResult(Request, segmentPath, FileShare.ReadWrite);
|
||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
||||
}
|
||||
|
||||
var segmentFilename = Path.GetFileName(segmentPath);
|
||||
|
@ -277,7 +287,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
// If it appears in the playlist, it's done
|
||||
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
{
|
||||
return ResultFactory.GetStaticFileResult(Request, segmentPath, FileShare.ReadWrite);
|
||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -286,7 +296,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
//var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath);
|
||||
//if (currentTranscodingIndex > segmentIndex)
|
||||
//{
|
||||
// return ResultFactory.GetStaticFileResult(Request, segmentPath, FileShare.ReadWrite);
|
||||
//return GetSegmentResult(segmentPath, segmentIndex);
|
||||
//}
|
||||
|
||||
// Wait for the file to stop being written to, then stream it
|
||||
|
@ -317,7 +327,27 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
await Task.Delay(100, cancellationToken).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, segmentPath, FileShare.ReadWrite);
|
||||
return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
|
||||
}
|
||||
|
||||
private object GetSegmentResult(string segmentPath, int index, int segmentLength, TranscodingJob transcodingJob)
|
||||
{
|
||||
var segmentEndingSeconds = (1 + index) * segmentLength;
|
||||
var segmentEndingPositionTicks = TimeSpan.FromSeconds(segmentEndingSeconds).Ticks;
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
|
||||
{
|
||||
Path = segmentPath,
|
||||
FileShare = FileShare.ReadWrite,
|
||||
OnComplete = () =>
|
||||
{
|
||||
if (transcodingJob != null)
|
||||
{
|
||||
transcodingJob.DownloadPositionTicks = Math.Max(transcodingJob.DownloadPositionTicks ?? segmentEndingPositionTicks, segmentEndingPositionTicks);
|
||||
}
|
||||
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private bool IsTranscoding(string playlistPath)
|
||||
|
@ -621,14 +651,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
return args;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command line arguments.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
|
||||
{
|
||||
var threads = GetNumberOfThreads(state, false);
|
||||
|
||||
|
@ -639,7 +662,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
var args = string.Format("{0} -i {1} -map_metadata -1 -threads {2} {3} {4} -copyts -flags -global_header {5} -hls_time {6} -start_number {7} -hls_list_size {8} -y \"{9}\"",
|
||||
inputModifier,
|
||||
GetInputArgument(state),
|
||||
GetInputArgument(transcodingJobId, state),
|
||||
threads,
|
||||
GetMapArgs(state),
|
||||
GetVideoArguments(state),
|
||||
|
|
|
@ -63,7 +63,17 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
public object Get(GetHlsPlaylist request)
|
||||
{
|
||||
OnBeginRequest(request.PlaylistId);
|
||||
var normalizedPlaylistId = request.PlaylistId.Replace("-low", string.Empty);
|
||||
|
||||
foreach (var playlist in Directory.EnumerateFiles(_appPaths.TranscodingTempPath, "*.m3u8")
|
||||
.Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
.ToList())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(playlist))
|
||||
{
|
||||
ExtendPlaylistTimer(playlist);
|
||||
}
|
||||
}
|
||||
|
||||
var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
|
||||
|
||||
|
@ -93,32 +103,16 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [begin request].
|
||||
/// </summary>
|
||||
/// <param name="playlistId">The playlist id.</param>
|
||||
protected void OnBeginRequest(string playlistId)
|
||||
{
|
||||
var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
|
||||
|
||||
foreach (var playlist in Directory.EnumerateFiles(_appPaths.TranscodingTempPath, "*.m3u8")
|
||||
.Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
.ToList())
|
||||
{
|
||||
if (!string.IsNullOrEmpty(playlist))
|
||||
{
|
||||
ExtendPlaylistTimer(playlist);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void ExtendPlaylistTimer(string playlist)
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
var job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
|
||||
await Task.Delay(20000).ConfigureAwait(false);
|
||||
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
|
||||
if (job != null)
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,18 +81,7 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
|
||||
file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file);
|
||||
|
||||
OnBeginRequest(request.PlaylistId);
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, file);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called when [begin request].
|
||||
/// </summary>
|
||||
/// <param name="playlistId">The playlist id.</param>
|
||||
protected void OnBeginRequest(string playlistId)
|
||||
{
|
||||
var normalizedPlaylistId = playlistId.Replace("-low", string.Empty);
|
||||
var normalizedPlaylistId = request.PlaylistId.Replace("-low", string.Empty);
|
||||
|
||||
foreach (var playlist in Directory.EnumerateFiles(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, "*.m3u8")
|
||||
.Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
|
||||
|
@ -100,15 +89,20 @@ namespace MediaBrowser.Api.Playback.Hls
|
|||
{
|
||||
ExtendPlaylistTimer(playlist);
|
||||
}
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, file);
|
||||
}
|
||||
|
||||
private async void ExtendPlaylistTimer(string playlist)
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
var job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
|
||||
|
||||
await Task.Delay(20000).ConfigureAwait(false);
|
||||
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls);
|
||||
if (job != null)
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
@ -55,15 +55,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
return ProcessRequest(request, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command line arguments.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||
/// <returns>System.String.</returns>
|
||||
/// <exception cref="System.InvalidOperationException">Only aac and mp3 audio codecs are supported.</exception>
|
||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
|
||||
{
|
||||
var audioTranscodeParams = new List<string>();
|
||||
|
||||
|
@ -92,7 +84,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
return string.Format("{0} -i {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
|
||||
inputModifier,
|
||||
GetInputArgument(state),
|
||||
GetInputArgument(transcodingJobId, state),
|
||||
threads,
|
||||
vn,
|
||||
string.Join(" ", audioTranscodeParams.ToArray()),
|
||||
|
|
|
@ -142,10 +142,9 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
var outputPath = state.OutputFilePath;
|
||||
var outputPathExists = File.Exists(outputPath);
|
||||
|
||||
var isStatic = request.Static ||
|
||||
(outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive));
|
||||
var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);
|
||||
|
||||
AddDlnaHeaders(state, responseHeaders, isStatic);
|
||||
AddDlnaHeaders(state, responseHeaders, request.Static || isTranscodeCached);
|
||||
|
||||
// Static stream
|
||||
if (request.Static)
|
||||
|
@ -154,6 +153,10 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
using (state)
|
||||
{
|
||||
var job = string.IsNullOrEmpty(request.TranscodingJobId) ?
|
||||
null :
|
||||
ApiEntryPoint.Instance.GetTranscodingJob(request.TranscodingJobId);
|
||||
|
||||
var limits = new List<long>();
|
||||
if (state.InputBitrate.HasValue)
|
||||
{
|
||||
|
@ -172,7 +175,13 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
}
|
||||
|
||||
// Take the greater of the above to methods, just to be safe
|
||||
var throttleLimit = limits.Count > 0 ? limits.Max() : 0;
|
||||
var throttleLimit = limits.Count > 0 ? limits.First() : 0;
|
||||
|
||||
// Pad to play it safe
|
||||
var bytesPerSecond = Convert.ToInt64(1.05 * throttleLimit);
|
||||
|
||||
// Don't even start evaluating this until at least two minutes have content have been consumed
|
||||
var targetGap = throttleLimit * 120;
|
||||
|
||||
return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
|
||||
{
|
||||
|
@ -182,17 +191,17 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
Path = state.MediaPath,
|
||||
Throttle = request.Throttle,
|
||||
|
||||
// Pad by 20% to play it safe
|
||||
ThrottleLimit = Convert.ToInt64(1.2 * throttleLimit),
|
||||
ThrottleLimit = bytesPerSecond,
|
||||
|
||||
// 3.5 minutes
|
||||
MinThrottlePosition = throttleLimit * 210
|
||||
MinThrottlePosition = targetGap,
|
||||
|
||||
ThrottleCallback = (l1, l2) => ThrottleCallack(l1, l2, bytesPerSecond, job)
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Not static but transcode cache file exists
|
||||
if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive))
|
||||
if (isTranscodeCached)
|
||||
{
|
||||
var contentType = state.GetMimeType(outputPath);
|
||||
|
||||
|
@ -225,6 +234,67 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
}
|
||||
}
|
||||
|
||||
private readonly long _gapLengthInTicks = TimeSpan.FromMinutes(3).Ticks;
|
||||
|
||||
private long ThrottleCallack(long currentBytesPerSecond, long bytesWritten, long originalBytesPerSecond, TranscodingJob job)
|
||||
{
|
||||
var bytesDownloaded = job.BytesDownloaded ?? 0;
|
||||
var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0;
|
||||
var downloadPositionTicks = job.DownloadPositionTicks ?? 0;
|
||||
|
||||
var path = job.Path;
|
||||
|
||||
if (bytesDownloaded > 0 && transcodingPositionTicks > 0)
|
||||
{
|
||||
// Progressive Streaming - byte-based consideration
|
||||
|
||||
try
|
||||
{
|
||||
var bytesTranscoded = job.BytesTranscoded ?? new FileInfo(path).Length;
|
||||
|
||||
// Estimate the bytes the transcoder should be ahead
|
||||
double gapFactor = _gapLengthInTicks;
|
||||
gapFactor /= transcodingPositionTicks;
|
||||
var targetGap = bytesTranscoded * gapFactor;
|
||||
|
||||
var gap = bytesTranscoded - bytesDownloaded;
|
||||
|
||||
if (gap < targetGap)
|
||||
{
|
||||
//Logger.Debug("Not throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Logger.Debug("Throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded);
|
||||
}
|
||||
catch
|
||||
{
|
||||
//Logger.Error("Error getting output size");
|
||||
}
|
||||
}
|
||||
else if (downloadPositionTicks > 0 && transcodingPositionTicks > 0)
|
||||
{
|
||||
// HLS - time-based consideration
|
||||
|
||||
var targetGap = _gapLengthInTicks;
|
||||
var gap = transcodingPositionTicks - downloadPositionTicks;
|
||||
|
||||
if (gap < targetGap)
|
||||
{
|
||||
//Logger.Debug("Not throttling transcoder gap {0} target gap {1}", gap, targetGap);
|
||||
return 0;
|
||||
}
|
||||
|
||||
//Logger.Debug("Throttling transcoder gap {0} target gap {1}", gap, targetGap);
|
||||
}
|
||||
else
|
||||
{
|
||||
//Logger.Debug("No throttle data for " + path);
|
||||
}
|
||||
|
||||
return originalBytesPerSecond;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the static remote stream result.
|
||||
/// </summary>
|
||||
|
@ -325,17 +395,18 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
TranscodingJob job;
|
||||
|
||||
if (!File.Exists(outputPath))
|
||||
{
|
||||
await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
|
||||
job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
|
||||
job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
|
||||
state.Dispose();
|
||||
}
|
||||
|
||||
var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
|
||||
var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job);
|
||||
|
||||
result.Options["Content-Type"] = contentType;
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using MediaBrowser.Common.IO;
|
||||
using MediaBrowser.Model.Logging;
|
||||
using ServiceStack.Web;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
|
@ -73,7 +74,10 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
}
|
||||
finally
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(Path, TranscodingJobType.Progressive);
|
||||
if (_job != null)
|
||||
{
|
||||
ApiEntryPoint.Instance.OnTranscodeEndRequest(_job);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -83,6 +87,8 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
private readonly IFileSystem _fileSystem;
|
||||
private readonly TranscodingJob _job;
|
||||
|
||||
private long _bytesWritten = 0;
|
||||
|
||||
public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job)
|
||||
{
|
||||
_fileSystem = fileSystem;
|
||||
|
@ -98,7 +104,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
{
|
||||
while (eofCount < 15)
|
||||
{
|
||||
await fs.CopyToAsync(outputStream).ConfigureAwait(false);
|
||||
await CopyToAsyncInternal(fs, outputStream, 81920, CancellationToken.None).ConfigureAwait(false);
|
||||
|
||||
var fsPosition = fs.Position;
|
||||
|
||||
|
@ -123,5 +129,22 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task CopyToAsyncInternal(Stream source, Stream destination, int bufferSize, CancellationToken cancellationToken)
|
||||
{
|
||||
byte[] array = new byte[bufferSize];
|
||||
int count;
|
||||
while ((count = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0)
|
||||
{
|
||||
await destination.WriteAsync(array, 0, count, cancellationToken).ConfigureAwait(false);
|
||||
|
||||
_bytesWritten += count;
|
||||
|
||||
if (_job != null)
|
||||
{
|
||||
_job.BytesDownloaded = Math.Max(_job.BytesDownloaded ?? _bytesWritten, _bytesWritten);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -84,14 +84,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
return ProcessRequest(request, true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the command line arguments.
|
||||
/// </summary>
|
||||
/// <param name="outputPath">The output path.</param>
|
||||
/// <param name="state">The state.</param>
|
||||
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
|
||||
/// <returns>System.String.</returns>
|
||||
protected override string GetCommandLineArguments(string outputPath, StreamState state, bool isEncoding)
|
||||
protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
|
||||
{
|
||||
// Get the output codec name
|
||||
var videoCodec = state.OutputVideoCodec;
|
||||
|
@ -110,7 +103,7 @@ namespace MediaBrowser.Api.Playback.Progressive
|
|||
|
||||
return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"",
|
||||
inputModifier,
|
||||
GetInputArgument(state),
|
||||
GetInputArgument(transcodingJobId, state),
|
||||
keyFrame,
|
||||
GetMapArgs(state),
|
||||
GetVideoArguments(state, videoCodec),
|
||||
|
|
|
@ -72,6 +72,7 @@ namespace MediaBrowser.Api.Playback
|
|||
public string Params { get; set; }
|
||||
|
||||
public bool Throttle { get; set; }
|
||||
public string TranscodingJobId { get; set; }
|
||||
}
|
||||
|
||||
public class VideoStreamRequest : StreamRequest
|
||||
|
|
|
@ -205,7 +205,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
list.Add(await GetUserView(CollectionType.MovieMovies, user, "2", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.MovieCollections, user, "3", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.MovieFavorites, user, "4", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.MovieGenres, user, "5", parent).ConfigureAwait(false));
|
||||
//list.Add(await GetUserView(CollectionType.MovieGenres, user, "5", parent).ConfigureAwait(false));
|
||||
|
||||
return GetResult(list, query);
|
||||
}
|
||||
|
@ -283,7 +283,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
list.Add(await GetUserView(CollectionType.TvLatest, user, "2", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.TvSeries, user, "3", parent).ConfigureAwait(false));
|
||||
//list.Add(await GetUserView(CollectionType.TvFavorites, user, "4", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.TvGenres, user, "5", parent).ConfigureAwait(false));
|
||||
//list.Add(await GetUserView(CollectionType.TvGenres, user, "5", parent).ConfigureAwait(false));
|
||||
|
||||
return GetResult(list, query);
|
||||
}
|
||||
|
@ -301,7 +301,7 @@ namespace MediaBrowser.Controller.Entities
|
|||
list.Add(await GetUserView(CollectionType.RecentlyPlayedGames, user, "1", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.GameFavorites, user, "2", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.GameSystems, user, "3", parent).ConfigureAwait(false));
|
||||
list.Add(await GetUserView(CollectionType.GameGenres, user, "4", parent).ConfigureAwait(false));
|
||||
//list.Add(await GetUserView(CollectionType.GameGenres, user, "4", parent).ConfigureAwait(false));
|
||||
|
||||
return GetResult(list, query);
|
||||
}
|
||||
|
|
|
@ -363,7 +363,7 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i
|
|||
<PreBuildEvent>
|
||||
</PreBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
|
@ -21,6 +21,9 @@ namespace MediaBrowser.Controller.Net
|
|||
public bool Throttle { get; set; }
|
||||
public long ThrottleLimit { get; set; }
|
||||
public long MinThrottlePosition { get; set; }
|
||||
public Func<long, long, long> ThrottleCallback { get; set; }
|
||||
|
||||
public Action OnComplete { get; set; }
|
||||
|
||||
public StaticResultOptions()
|
||||
{
|
||||
|
|
|
@ -22,8 +22,6 @@ namespace MediaBrowser.Dlna.Ssdp
|
|||
/// </summary>
|
||||
public int SendCount { get; private set; }
|
||||
|
||||
public bool HandleBindError { get; set; }
|
||||
|
||||
private readonly ILogger _logger;
|
||||
|
||||
public Datagram(IPEndPoint toEndPoint, IPEndPoint fromEndPoint, ILogger logger, string message, int totalSendCount)
|
||||
|
@ -44,17 +42,7 @@ namespace MediaBrowser.Dlna.Ssdp
|
|||
|
||||
if (FromEndPoint != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
client.Bind(FromEndPoint);
|
||||
}
|
||||
catch
|
||||
{
|
||||
if (!HandleBindError)
|
||||
{
|
||||
throw;
|
||||
}
|
||||
}
|
||||
client.Bind(FromEndPoint);
|
||||
}
|
||||
|
||||
client.BeginSendTo(msg, 0, msg.Length, SocketFlags.None, ToEndPoint, result =>
|
||||
|
|
|
@ -124,22 +124,18 @@ namespace MediaBrowser.Dlna.Ssdp
|
|||
IPEndPoint localAddress,
|
||||
int sendCount = 1)
|
||||
{
|
||||
SendDatagram(header, values, _ssdpEndp, localAddress, false, sendCount);
|
||||
SendDatagram(header, values, _ssdpEndp, localAddress, sendCount);
|
||||
}
|
||||
|
||||
public void SendDatagram(string header,
|
||||
Dictionary<string, string> values,
|
||||
IPEndPoint endpoint,
|
||||
IPEndPoint localAddress,
|
||||
bool handleBindError,
|
||||
int sendCount = 1)
|
||||
{
|
||||
var msg = new SsdpMessageBuilder().BuildMessage(header, values);
|
||||
|
||||
var dgram = new Datagram(endpoint, localAddress, _logger, msg, sendCount)
|
||||
{
|
||||
HandleBindError = handleBindError
|
||||
};
|
||||
var dgram = new Datagram(endpoint, localAddress, _logger, msg, sendCount);
|
||||
|
||||
if (_messageQueue.Count == 0)
|
||||
{
|
||||
|
@ -175,7 +171,7 @@ namespace MediaBrowser.Dlna.Ssdp
|
|||
values["ST"] = d.Type;
|
||||
values["USN"] = d.USN;
|
||||
|
||||
SendDatagram(header, values, endpoint, new IPEndPoint(d.Address, 0), true);
|
||||
SendDatagram(header, values, endpoint, null);
|
||||
|
||||
if (_config.GetDlnaConfiguration().EnableDebugLogging)
|
||||
{
|
||||
|
|
|
@ -91,7 +91,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
|
@ -372,7 +372,7 @@
|
|||
xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\net45\" /y /d /r /i
|
||||
)</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<Import Project="Fody.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
|
|
|
@ -211,7 +211,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
|
@ -378,8 +378,8 @@ namespace MediaBrowser.Server.Implementations.Dto
|
|||
|
||||
if (album != null)
|
||||
{
|
||||
dto.Album = item.Name;
|
||||
dto.AlbumId = item.Id.ToString("N");
|
||||
dto.Album = album.Name;
|
||||
dto.AlbumId = album.Id.ToString("N");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -102,14 +102,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
return result;
|
||||
}
|
||||
|
||||
private bool SupportsCompression
|
||||
{
|
||||
get
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the optimized result.
|
||||
/// </summary>
|
||||
|
@ -127,7 +119,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
throw new ArgumentNullException("result");
|
||||
}
|
||||
|
||||
var optimizedResult = SupportsCompression ? requestContext.ToOptimizedResult(result) : result;
|
||||
var optimizedResult = requestContext.ToOptimizedResult(result);
|
||||
|
||||
if (responseHeaders != null)
|
||||
{
|
||||
|
@ -471,7 +463,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
{
|
||||
Throttle = options.Throttle,
|
||||
ThrottleLimit = options.ThrottleLimit,
|
||||
MinThrottlePosition = options.MinThrottlePosition
|
||||
MinThrottlePosition = options.MinThrottlePosition,
|
||||
ThrottleCallback = options.ThrottleCallback,
|
||||
OnComplete = options.OnComplete
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -488,41 +482,22 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
{
|
||||
Throttle = options.Throttle,
|
||||
ThrottleLimit = options.ThrottleLimit,
|
||||
MinThrottlePosition = options.MinThrottlePosition
|
||||
MinThrottlePosition = options.MinThrottlePosition,
|
||||
ThrottleCallback = options.ThrottleCallback,
|
||||
OnComplete = options.OnComplete
|
||||
};
|
||||
}
|
||||
|
||||
string content;
|
||||
long originalContentLength = 0;
|
||||
|
||||
using (var stream = await factoryFn().ConfigureAwait(false))
|
||||
{
|
||||
using (var memoryStream = new MemoryStream())
|
||||
using (var reader = new StreamReader(stream))
|
||||
{
|
||||
await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
|
||||
memoryStream.Position = 0;
|
||||
|
||||
originalContentLength = memoryStream.Length;
|
||||
|
||||
using (var reader = new StreamReader(memoryStream))
|
||||
{
|
||||
content = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
content = await reader.ReadToEndAsync().ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!SupportsCompression)
|
||||
{
|
||||
responseHeaders["Content-Length"] = originalContentLength.ToString(UsCulture);
|
||||
|
||||
if (isHeadRequest)
|
||||
{
|
||||
return GetHttpResult(new byte[] { }, contentType);
|
||||
}
|
||||
|
||||
return new HttpResult(content, contentType);
|
||||
}
|
||||
|
||||
var contents = content.Compress(requestedCompressionType);
|
||||
|
||||
responseHeaders["Content-Length"] = contents.Length.ToString(UsCulture);
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
public bool Throttle { get; set; }
|
||||
public long ThrottleLimit { get; set; }
|
||||
public long MinThrottlePosition;
|
||||
public Func<long, long, long> ThrottleCallback { get; set; }
|
||||
public Action OnComplete { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The _options
|
||||
|
@ -167,7 +169,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
{
|
||||
responseStream = new ThrottledStream(responseStream, ThrottleLimit)
|
||||
{
|
||||
MinThrottlePosition = MinThrottlePosition
|
||||
MinThrottlePosition = MinThrottlePosition,
|
||||
ThrottleCallback = ThrottleCallback
|
||||
};
|
||||
}
|
||||
var task = WriteToAsync(responseStream);
|
||||
|
@ -182,22 +185,32 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
/// <returns>Task.</returns>
|
||||
private async Task WriteToAsync(Stream responseStream)
|
||||
{
|
||||
// Headers only
|
||||
if (IsHeadRequest)
|
||||
try
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
using (var source = SourceStream)
|
||||
{
|
||||
// If the requested range is "0-", we can optimize by just doing a stream copy
|
||||
if (RangeEnd >= TotalContentLength - 1)
|
||||
// Headers only
|
||||
if (IsHeadRequest)
|
||||
{
|
||||
await source.CopyToAsync(responseStream).ConfigureAwait(false);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
||||
using (var source = SourceStream)
|
||||
{
|
||||
await CopyToAsyncInternal(source, responseStream, Convert.ToInt32(RangeLength), CancellationToken.None).ConfigureAwait(false);
|
||||
// If the requested range is "0-", we can optimize by just doing a stream copy
|
||||
if (RangeEnd >= TotalContentLength - 1)
|
||||
{
|
||||
await source.CopyToAsync(responseStream).ConfigureAwait(false);
|
||||
}
|
||||
else
|
||||
{
|
||||
await CopyToAsyncInternal(source, responseStream, Convert.ToInt32(RangeLength), CancellationToken.None).ConfigureAwait(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (OnComplete != null)
|
||||
{
|
||||
OnComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,6 +39,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
public bool Throttle { get; set; }
|
||||
public long ThrottleLimit { get; set; }
|
||||
public long MinThrottlePosition;
|
||||
public Func<long, long, long> ThrottleCallback { get; set; }
|
||||
public Action OnComplete { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="StreamWriter" /> class.
|
||||
|
@ -85,7 +87,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
{
|
||||
responseStream = new ThrottledStream(responseStream, ThrottleLimit)
|
||||
{
|
||||
MinThrottlePosition = MinThrottlePosition
|
||||
MinThrottlePosition = MinThrottlePosition,
|
||||
ThrottleCallback = ThrottleCallback
|
||||
};
|
||||
}
|
||||
var task = WriteToAsync(responseStream);
|
||||
|
@ -113,6 +116,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
|
||||
throw;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (OnComplete != null)
|
||||
{
|
||||
OnComplete();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
/// </summary>
|
||||
public const long Infinite = 0;
|
||||
|
||||
public Func<long, long, long> ThrottleCallback { get; set; }
|
||||
|
||||
#region Private members
|
||||
/// <summary>
|
||||
/// The base stream.
|
||||
|
@ -278,6 +280,32 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
}
|
||||
#endregion
|
||||
|
||||
private bool ThrottleCheck(int bufferSizeInBytes)
|
||||
{
|
||||
if (_bytesWritten < MinThrottlePosition)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Make sure the buffer isn't empty.
|
||||
if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (ThrottleCallback != null)
|
||||
{
|
||||
var val = ThrottleCallback(_maximumBytesPerSecond, _bytesWritten);
|
||||
|
||||
if (val == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#region Protected methods
|
||||
/// <summary>
|
||||
/// Throttles for the specified buffer size in bytes.
|
||||
|
@ -285,15 +313,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
/// <param name="bufferSizeInBytes">The buffer size in bytes.</param>
|
||||
protected void Throttle(int bufferSizeInBytes)
|
||||
{
|
||||
if (_bytesWritten < MinThrottlePosition)
|
||||
if (!ThrottleCheck(bufferSizeInBytes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the buffer isn't empty.
|
||||
if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
|
||||
{
|
||||
return;
|
||||
return ;
|
||||
}
|
||||
|
||||
_byteCount += bufferSizeInBytes;
|
||||
|
@ -332,13 +354,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
|
|||
|
||||
protected async Task ThrottleAsync(int bufferSizeInBytes, CancellationToken cancellationToken)
|
||||
{
|
||||
if (_bytesWritten < MinThrottlePosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Make sure the buffer isn't empty.
|
||||
if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
|
||||
if (!ThrottleCheck(bufferSizeInBytes))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -501,7 +501,7 @@
|
|||
</ItemGroup>
|
||||
<ItemGroup />
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
|
@ -2136,7 +2136,7 @@
|
|||
<PostBuildEvent>
|
||||
</PostBuildEvent>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " />
|
||||
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
|
||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||
Other similar extension points exist, see Microsoft.Common.targets.
|
||||
<Target Name="BeforeBuild">
|
||||
|
|
Loading…
Reference in New Issue
Block a user