aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLuke Pulverenti <luke.pulverenti@gmail.com>2014-09-02 22:30:24 -0400
committerLuke Pulverenti <luke.pulverenti@gmail.com>2014-09-02 22:30:24 -0400
commit60d1d5cdee642ee9d5be7e91be5caeb9a1a756df (patch)
tree67dcd2270977afafc596a1c4568cdaa4cf9d26dc
parenta3d553a7fbe93fe416c940e80d5e511a2d753f25 (diff)
restore nuget targets for mono build
-rw-r--r--MediaBrowser.Api/ApiEntryPoint.cs94
-rw-r--r--MediaBrowser.Api/MediaBrowser.Api.csproj2
-rw-r--r--MediaBrowser.Api/Playback/BaseStreamingService.cs63
-rw-r--r--MediaBrowser.Api/Playback/Hls/BaseHlsService.cs28
-rw-r--r--MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs63
-rw-r--r--MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs38
-rw-r--r--MediaBrowser.Api/Playback/Hls/VideoHlsService.cs22
-rw-r--r--MediaBrowser.Api/Playback/Progressive/AudioService.cs12
-rw-r--r--MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs95
-rw-r--r--MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs29
-rw-r--r--MediaBrowser.Api/Playback/Progressive/VideoService.cs11
-rw-r--r--MediaBrowser.Api/Playback/StreamRequest.cs1
-rw-r--r--MediaBrowser.Controller/Entities/UserViewBuilder.cs6
-rw-r--r--MediaBrowser.Controller/MediaBrowser.Controller.csproj2
-rw-r--r--MediaBrowser.Controller/Net/StaticResultOptions.cs3
-rw-r--r--MediaBrowser.Dlna/Ssdp/Datagram.cs14
-rw-r--r--MediaBrowser.Dlna/Ssdp/SsdpHandler.cs10
-rw-r--r--MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj2
-rw-r--r--MediaBrowser.Model/MediaBrowser.Model.csproj2
-rw-r--r--MediaBrowser.Providers/MediaBrowser.Providers.csproj2
-rw-r--r--MediaBrowser.Server.Implementations/Dto/DtoService.cs4
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs43
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs37
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs12
-rw-r--r--MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs46
-rw-r--r--MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj2
-rw-r--r--MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj2
27 files changed, 395 insertions, 250 deletions
diff --git a/MediaBrowser.Api/ApiEntryPoint.cs b/MediaBrowser.Api/ApiEntryPoint.cs
index c51d9e7c0..b49fbce48 100644
--- a/MediaBrowser.Api/ApiEntryPoint.cs
+++ b/MediaBrowser.Api/ApiEntryPoint.cs
@@ -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
+ };
+
+ _activeTranscodingJobs.Add(job);
+
+ ReportTranscodingProgress(job, state, null, null, null, null);
- ReportTranscodingProgress(state, 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>
diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj
index b0131b5c2..5cb9ebb1b 100644
--- a/MediaBrowser.Api/MediaBrowser.Api.csproj
+++ b/MediaBrowser.Api/MediaBrowser.Api.csproj
@@ -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">
diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index 19f9db636..5cae6b010 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -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;
diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
index bd17dc0b2..a3a86ba2e 100644
--- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs
@@ -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),
diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
index 134c28524..40ec89f55 100644
--- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs
@@ -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),
diff --git a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
index f28352588..12b2b8dae 100644
--- a/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
+++ b/MediaBrowser.Api/Playback/Hls/HlsSegmentService.cs
@@ -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);
+ }
}
}
}
diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
index 346af016e..3581b9e17 100644
--- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
+++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs
@@ -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>
diff --git a/MediaBrowser.Api/Playback/Progressive/AudioService.cs b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
index 6fae65ffc..ae592c428 100644
--- a/MediaBrowser.Api/Playback/Progressive/AudioService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/AudioService.cs
@@ -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()),
diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
index f0ad6ce5c..50929e6f3 100644
--- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs
@@ -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;
diff --git a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
index a62f3dc9f..6e77e5eab 100644
--- a/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
+++ b/MediaBrowser.Api/Playback/Progressive/ProgressiveStreamWriter.cs
@@ -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);
+ }
+ }
+ }
}
}
diff --git a/MediaBrowser.Api/Playback/Progressive/VideoService.cs b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
index 639381164..f82de5a6a 100644
--- a/MediaBrowser.Api/Playback/Progressive/VideoService.cs
+++ b/MediaBrowser.Api/Playback/Progressive/VideoService.cs
@@ -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),
diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs
index 0de8c28a9..7568afa46 100644
--- a/MediaBrowser.Api/Playback/StreamRequest.cs
+++ b/MediaBrowser.Api/Playback/StreamRequest.cs
@@ -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
diff --git a/MediaBrowser.Controller/Entities/UserViewBuilder.cs b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
index f11ac70ca..bc97a43b7 100644
--- a/MediaBrowser.Controller/Entities/UserViewBuilder.cs
+++ b/MediaBrowser.Controller/Entities/UserViewBuilder.cs
@@ -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);
}
diff --git a/MediaBrowser.Controller/MediaBrowser.Controller.csproj b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
index 2ad950a4f..1813d9f08 100644
--- a/MediaBrowser.Controller/MediaBrowser.Controller.csproj
+++ b/MediaBrowser.Controller/MediaBrowser.Controller.csproj
@@ -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">
diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs
index fde08c269..5bb2c9a5c 100644
--- a/MediaBrowser.Controller/Net/StaticResultOptions.cs
+++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs
@@ -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()
{
diff --git a/MediaBrowser.Dlna/Ssdp/Datagram.cs b/MediaBrowser.Dlna/Ssdp/Datagram.cs
index 45f571b4b..2554d33c6 100644
--- a/MediaBrowser.Dlna/Ssdp/Datagram.cs
+++ b/MediaBrowser.Dlna/Ssdp/Datagram.cs
@@ -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 =>
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
index bee4a3a56..bac920ecb 100644
--- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
+++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
@@ -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)
{
diff --git a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
index cc753298c..22873d910 100644
--- a/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
+++ b/MediaBrowser.MediaEncoding/MediaBrowser.MediaEncoding.csproj
@@ -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">
diff --git a/MediaBrowser.Model/MediaBrowser.Model.csproj b/MediaBrowser.Model/MediaBrowser.Model.csproj
index 082f64bd9..a760cba05 100644
--- a/MediaBrowser.Model/MediaBrowser.Model.csproj
+++ b/MediaBrowser.Model/MediaBrowser.Model.csproj
@@ -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.
diff --git a/MediaBrowser.Providers/MediaBrowser.Providers.csproj b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
index 995547ce6..b69a4bc2f 100644
--- a/MediaBrowser.Providers/MediaBrowser.Providers.csproj
+++ b/MediaBrowser.Providers/MediaBrowser.Providers.csproj
@@ -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">
diff --git a/MediaBrowser.Server.Implementations/Dto/DtoService.cs b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
index 922287f6e..7f04790de 100644
--- a/MediaBrowser.Server.Implementations/Dto/DtoService.cs
+++ b/MediaBrowser.Server.Implementations/Dto/DtoService.cs
@@ -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");
}
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
index 9997cfbdb..e13e27d5a 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/HttpResultFactory.cs
@@ -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,39 +482,20 @@ 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())
- {
- await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
- memoryStream.Position = 0;
-
- originalContentLength = memoryStream.Length;
-
- using (var reader = new StreamReader(memoryStream))
- {
- content = await reader.ReadToEndAsync().ConfigureAwait(false);
- }
- }
- }
-
- if (!SupportsCompression)
- {
- responseHeaders["Content-Length"] = originalContentLength.ToString(UsCulture);
-
- if (isHeadRequest)
+ using (var reader = new StreamReader(stream))
{
- return GetHttpResult(new byte[] { }, contentType);
+ content = await reader.ReadToEndAsync().ConfigureAwait(false);
}
-
- return new HttpResult(content, contentType);
}
var contents = content.Compress(requestedCompressionType);
diff --git a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
index 657545069..7a143c8d9 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/RangeRequestWriter.cs
@@ -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;
- }
+ // Headers only
+ if (IsHeadRequest)
+ {
+ return;
+ }
- using (var source = SourceStream)
- {
- // If the requested range is "0-", we can optimize by just doing a stream copy
- if (RangeEnd >= TotalContentLength - 1)
+ using (var source = SourceStream)
{
- await source.CopyToAsync(responseStream).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);
+ }
}
- else
+ }
+ finally
+ {
+ if (OnComplete != null)
{
- await CopyToAsyncInternal(source, responseStream, Convert.ToInt32(RangeLength), CancellationToken.None).ConfigureAwait(false);
+ OnComplete();
}
}
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs b/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs
index 28fc094f7..1ca5f1204 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/StreamWriter.cs
@@ -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();
+ }
+ }
}
}
}
diff --git a/MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs b/MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs
index 067e53571..4bde30dac 100644
--- a/MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs
+++ b/MediaBrowser.Server.Implementations/HttpServer/ThrottledStream.cs
@@ -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,22 +280,42 @@ namespace MediaBrowser.Server.Implementations.HttpServer
}
#endregion
- #region Protected methods
- /// <summary>
- /// Throttles for the specified buffer size in bytes.
- /// </summary>
- /// <param name="bufferSizeInBytes">The buffer size in bytes.</param>
- protected void Throttle(int bufferSizeInBytes)
+ private bool ThrottleCheck(int bufferSizeInBytes)
{
if (_bytesWritten < MinThrottlePosition)
{
- return;
+ return false;
}
// Make sure the buffer isn't empty.
if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
{
- return;
+ 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.
+ /// </summary>
+ /// <param name="bufferSizeInBytes">The buffer size in bytes.</param>
+ protected void Throttle(int bufferSizeInBytes)
+ {
+ if (!ThrottleCheck(bufferSizeInBytes))
+ {
+ 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;
}
diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
index a03cae78c..313063673 100644
--- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
+++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj
@@ -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">
diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
index 864a2f70f..32cb88218 100644
--- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
+++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
@@ -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">