restore nuget targets for mono build

This commit is contained in:
Luke Pulverenti 2014-09-02 22:30:24 -04:00
parent a3d553a7fb
commit 60d1d5cdee
27 changed files with 396 additions and 251 deletions

View File

@ -120,12 +120,15 @@ namespace MediaBrowser.Api
/// Called when [transcode beginning]. /// Called when [transcode beginning].
/// </summary> /// </summary>
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <param name="transcodingJobId">The transcoding job identifier.</param>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
/// <param name="process">The process.</param> /// <param name="process">The process.</param>
/// <param name="deviceId">The device id.</param> /// <param name="deviceId">The device id.</param>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <param name="cancellationTokenSource">The cancellation token source.</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, TranscodingJobType type,
Process process, Process process,
string deviceId, string deviceId,
@ -134,22 +137,37 @@ namespace MediaBrowser.Api
{ {
lock (_activeTranscodingJobs) lock (_activeTranscodingJobs)
{ {
_activeTranscodingJobs.Add(new TranscodingJob var job = new TranscodingJob
{ {
Type = type, Type = type,
Path = path, Path = path,
Process = process, Process = process,
ActiveRequestCount = 1, ActiveRequestCount = 1,
DeviceId = deviceId, 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; var deviceId = state.Request.DeviceId;
if (!string.IsNullOrWhiteSpace(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> /// <summary>
/// Called when [transcode begin request]. /// Called when [transcode begin request].
/// </summary> /// </summary>
/// <param name="path">The path.</param> /// <param name="path">The path.</param>
/// <param name="type">The type.</param> /// <param name="type">The type.</param>
public void OnTranscodeBeginRequest(string path, TranscodingJobType type) public TranscodingJob OnTranscodeBeginRequest(string path, TranscodingJobType type)
{ {
lock (_activeTranscodingJobs) lock (_activeTranscodingJobs)
{ {
@ -239,7 +265,7 @@ namespace MediaBrowser.Api
if (job == null) if (job == null)
{ {
return; return null;
} }
job.ActiveRequestCount++; job.ActiveRequestCount++;
@ -249,31 +275,19 @@ namespace MediaBrowser.Api
job.KillTimer.Dispose(); job.KillTimer.Dispose();
job.KillTimer = null; job.KillTimer = null;
} }
return job;
} }
} }
/// <summary> public void OnTranscodeEndRequest(TranscodingJob job)
/// 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)
{ {
lock (_activeTranscodingJobs)
{
var job = _activeTranscodingJobs.FirstOrDefault(j => j.Type == type && j.Path.Equals(path, StringComparison.OrdinalIgnoreCase));
if (job == null)
{
return;
}
job.ActiveRequestCount--; job.ActiveRequestCount--;
if (job.ActiveRequestCount == 0) if (job.ActiveRequestCount == 0)
{ {
// The HLS kill timer is long - 1/2 hr. clients should use the manual kill command when stopping. // 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; var timerDuration = job.Type == TranscodingJobType.Progressive ? 1000 : 1800000;
if (job.KillTimer == null) if (job.KillTimer == null)
{ {
@ -285,7 +299,6 @@ namespace MediaBrowser.Api
} }
} }
} }
}
/// <summary> /// <summary>
/// Called when [transcode kill timer stopped]. /// Called when [transcode kill timer stopped].
@ -306,7 +319,6 @@ namespace MediaBrowser.Api
/// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param> /// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="ArgumentNullException">deviceId</exception> /// <exception cref="ArgumentNullException">deviceId</exception>
/// <exception cref="System.ArgumentNullException">sourcePath</exception>
internal Task KillTranscodingJobs(string deviceId, Func<string, bool> deleteFiles, bool acquireLock) internal Task KillTranscodingJobs(string deviceId, Func<string, bool> deleteFiles, bool acquireLock)
{ {
if (string.IsNullOrEmpty(deviceId)) if (string.IsNullOrEmpty(deviceId))
@ -324,8 +336,7 @@ namespace MediaBrowser.Api
/// <param name="deleteFiles">The delete files.</param> /// <param name="deleteFiles">The delete files.</param>
/// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param> /// <param name="acquireLock">if set to <c>true</c> [acquire lock].</param>
/// <returns>Task.</returns> /// <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>(); var jobs = new List<TranscodingJob>();
@ -542,6 +553,17 @@ namespace MediaBrowser.Api
public object ProcessLock = new object(); public object ProcessLock = new object();
public bool HasExited { get; set; } 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> /// <summary>

View File

@ -169,7 +169,7 @@
<PostBuildEvent> <PostBuildEvent>
</PostBuildEvent> </PostBuildEvent>
</PropertyGroup> </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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -90,10 +90,11 @@ namespace MediaBrowser.Api.Playback
/// Gets the command line arguments. /// Gets the command line arguments.
/// </summary> /// </summary>
/// <param name="outputPath">The output path.</param> /// <param name="outputPath">The output path.</param>
/// <param name="transcodingJobId">The transcoding job identifier.</param>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <param name="isEncoding">if set to <c>true</c> [is encoding].</param> /// <param name="isEncoding">if set to <c>true</c> [is encoding].</param>
/// <returns>System.String.</returns> /// <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> /// <summary>
/// Gets the type of the transcoding job. /// Gets the type of the transcoding job.
@ -122,7 +123,7 @@ namespace MediaBrowser.Api.Playback
var outputFileExtension = GetOutputFileExtension(state); 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); data += "-" + (state.Request.DeviceId ?? string.Empty);
@ -782,9 +783,10 @@ namespace MediaBrowser.Api.Playback
/// <summary> /// <summary>
/// Gets the input argument. /// Gets the input argument.
/// </summary> /// </summary>
/// <param name="transcodingJobId">The transcoding job identifier.</param>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <returns>System.String.</returns> /// <returns>System.String.</returns>
protected string GetInputArgument(StreamState state) protected string GetInputArgument(string transcodingJobId, StreamState state)
{ {
if (state.InputProtocol == MediaProtocol.File && if (state.InputProtocol == MediaProtocol.File &&
state.RunTimeTicks.HasValue && 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; 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); return string.Format("\"{0}\"", url);
} }
} }
@ -897,7 +901,7 @@ namespace MediaBrowser.Api.Playback
/// <param name="cancellationTokenSource">The cancellation token source.</param> /// <param name="cancellationTokenSource">The cancellation token source.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
/// <exception cref="System.InvalidOperationException">ffmpeg was not found at + MediaEncoder.EncoderPath</exception> /// <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)) if (!File.Exists(MediaEncoder.EncoderPath))
{ {
@ -908,7 +912,8 @@ namespace MediaBrowser.Api.Playback
await AcquireResources(state, cancellationTokenSource).ConfigureAwait(false); 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) if (ServerConfigurationManager.Configuration.EnableDebugEncodingLogging)
{ {
@ -938,7 +943,8 @@ namespace MediaBrowser.Api.Playback
EnableRaisingEvents = true EnableRaisingEvents = true
}; };
ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath, var transcodingJob = ApiEntryPoint.Instance.OnTranscodeBeginning(outputPath,
transcodingId,
TranscodingJobType, TranscodingJobType,
process, process,
state.Request.DeviceId, state.Request.DeviceId,
@ -957,7 +963,7 @@ namespace MediaBrowser.Api.Playback
var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine); var commandLineLogMessageBytes = Encoding.UTF8.GetBytes(commandLineLogMessage + Environment.NewLine + Environment.NewLine);
await state.LogFileStream.WriteAsync(commandLineLogMessageBytes, 0, commandLineLogMessageBytes.Length, cancellationTokenSource.Token).ConfigureAwait(false); 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 try
{ {
@ -976,16 +982,18 @@ namespace MediaBrowser.Api.Playback
process.BeginOutputReadLine(); process.BeginOutputReadLine();
// Important - don't await the log task or we won't be able to kill ffmpeg when the user stops playback // 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 // Wait for the file to exist before proceeeding
while (!File.Exists(outputPath)) while (!File.Exists(outputPath))
{ {
await Task.Delay(100, cancellationTokenSource.Token).ConfigureAwait(false); 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 try
{ {
@ -995,7 +1003,7 @@ namespace MediaBrowser.Api.Playback
{ {
var line = await reader.ReadLineAsync().ConfigureAwait(false); var line = await reader.ReadLineAsync().ConfigureAwait(false);
ParseLogLine(line, state); ParseLogLine(line, transcodingJob, state);
var bytes = Encoding.UTF8.GetBytes(Environment.NewLine + line); 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; float? framerate = null;
double? percent = null; double? percent = null;
TimeSpan? transcodingPosition = null;
long? bytesTranscoded = null;
var parts = line.Split(' '); var parts = line.Split(' ');
@ -1051,13 +1061,36 @@ namespace MediaBrowser.Api.Playback
var percentVal = currentMs / totalMs; var percentVal = currentMs / totalMs;
percent = 100 * percentVal; 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) 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. /// Processes the exited.
/// </summary> /// </summary>
/// <param name="process">The process.</param> /// <param name="process">The process.</param>
/// <param name="job">The job.</param>
/// <param name="state">The state.</param> /// <param name="state">The state.</param>
/// <param name="outputPath">The output path.</param> private void OnFfMpegProcessExited(Process process, TranscodingJob job, StreamState state)
private void OnFfMpegProcessExited(Process process, StreamState state, string outputPath)
{ {
var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType);
if (job != null) if (job != null)
{ {
job.HasExited = true; job.HasExited = true;

View File

@ -88,10 +88,11 @@ namespace MediaBrowser.Api.Playback.Hls
} }
var playlist = state.OutputFilePath; var playlist = state.OutputFilePath;
TranscodingJob job;
if (File.Exists(playlist)) if (File.Exists(playlist))
{ {
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
} }
else else
{ {
@ -100,14 +101,14 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
if (File.Exists(playlist)) if (File.Exists(playlist))
{ {
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlist, TranscodingJobType.Hls);
} }
else else
{ {
// If the playlist doesn't already exist, startup ffmpeg // If the playlist doesn't already exist, startup ffmpeg
try try
{ {
await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false); job = await StartFfMpeg(state, playlist, cancellationTokenSource).ConfigureAwait(false);
} }
catch catch
{ {
@ -137,7 +138,10 @@ namespace MediaBrowser.Api.Playback.Hls
} }
finally finally
{ {
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls); if (job != null)
{
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
}
} }
} }
@ -162,7 +166,10 @@ namespace MediaBrowser.Api.Playback.Hls
} }
finally finally
{ {
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls); if (job != null)
{
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
}
} }
} }
@ -241,14 +248,7 @@ namespace MediaBrowser.Api.Playback.Hls
} }
} }
/// <summary> protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
/// 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)
{ {
var hlsVideoRequest = state.VideoRequest as GetHlsVideoStream; 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}\"", 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, itsOffset,
inputModifier, inputModifier,
GetInputArgument(state), GetInputArgument(transcodingJobId, state),
threads, threads,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state),

View File

@ -6,6 +6,7 @@ using MediaBrowser.Controller.Dlna;
using MediaBrowser.Controller.Library; using MediaBrowser.Controller.Library;
using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.LiveTv;
using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.Controller.Net;
using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Dlna;
using MediaBrowser.Model.Entities; using MediaBrowser.Model.Entities;
using MediaBrowser.Model.IO; using MediaBrowser.Model.IO;
@ -107,11 +108,14 @@ namespace MediaBrowser.Api.Playback.Hls
var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8"); var playlistPath = Path.ChangeExtension(state.OutputFilePath, ".m3u8");
var segmentPath = GetSegmentPath(playlistPath, index); var segmentPath = GetSegmentPath(playlistPath, index);
var segmentLength = state.SegmentLength;
TranscodingJob job = null;
if (File.Exists(segmentPath)) if (File.Exists(segmentPath))
{ {
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
return await GetSegmentResult(playlistPath, segmentPath, index, cancellationToken).ConfigureAwait(false); return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
} }
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
@ -119,8 +123,8 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
if (File.Exists(segmentPath)) if (File.Exists(segmentPath))
{ {
ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(playlistPath, TranscodingJobType.Hls);
return await GetSegmentResult(playlistPath, segmentPath, index, cancellationToken).ConfigureAwait(false); return await GetSegmentResult(playlistPath, segmentPath, index, segmentLength, job, cancellationToken).ConfigureAwait(false);
} }
else else
{ {
@ -141,7 +145,7 @@ namespace MediaBrowser.Api.Playback.Hls
var startSeconds = index * state.SegmentLength; var startSeconds = index * state.SegmentLength;
request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks; request.StartTimeTicks = TimeSpan.FromSeconds(startSeconds).Ticks;
await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false); job = await StartFfMpeg(state, playlistPath, cancellationTokenSource).ConfigureAwait(false);
} }
catch catch
{ {
@ -165,7 +169,8 @@ namespace MediaBrowser.Api.Playback.Hls
} }
Logger.Info("returning {0}", segmentPath); 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) public int? GetCurrentTranscodingIndex(string playlist)
@ -258,12 +263,17 @@ namespace MediaBrowser.Api.Playback.Hls
return Path.Combine(folder, filename + index.ToString(UsCulture) + ".ts"); 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 all transcoding has completed, just return immediately
if (!IsTranscoding(playlistPath)) if (!IsTranscoding(playlistPath))
{ {
return ResultFactory.GetStaticFileResult(Request, segmentPath, FileShare.ReadWrite); return GetSegmentResult(segmentPath, segmentIndex, segmentLength, transcodingJob);
} }
var segmentFilename = Path.GetFileName(segmentPath); var segmentFilename = Path.GetFileName(segmentPath);
@ -277,7 +287,7 @@ namespace MediaBrowser.Api.Playback.Hls
// If it appears in the playlist, it's done // If it appears in the playlist, it's done
if (text.IndexOf(segmentFilename, StringComparison.OrdinalIgnoreCase) != -1) 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); //var currentTranscodingIndex = GetCurrentTranscodingIndex(playlistPath);
//if (currentTranscodingIndex > segmentIndex) //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 // 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); 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) private bool IsTranscoding(string playlistPath)
@ -621,14 +651,7 @@ namespace MediaBrowser.Api.Playback.Hls
return args; return args;
} }
/// <summary> protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
/// 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)
{ {
var threads = GetNumberOfThreads(state, false); 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}\"", 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, inputModifier,
GetInputArgument(state), GetInputArgument(transcodingJobId, state),
threads, threads,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state), GetVideoArguments(state),

View File

@ -63,7 +63,17 @@ namespace MediaBrowser.Api.Playback.Hls
public object Get(GetHlsPlaylist request) 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); var file = request.PlaylistId + Path.GetExtension(Request.PathInfo);
@ -93,32 +103,16 @@ namespace MediaBrowser.Api.Playback.Hls
return ResultFactory.GetStaticFileResult(Request, file, FileShare.ReadWrite); 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) 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); await Task.Delay(20000).ConfigureAwait(false);
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls); if (job != null)
{
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
}
} }
} }
} }

View File

@ -81,18 +81,7 @@ namespace MediaBrowser.Api.Playback.Hls
file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file); file = Path.Combine(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, file);
OnBeginRequest(request.PlaylistId); var normalizedPlaylistId = request.PlaylistId.Replace("-low", string.Empty);
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);
foreach (var playlist in Directory.EnumerateFiles(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, "*.m3u8") foreach (var playlist in Directory.EnumerateFiles(ServerConfigurationManager.ApplicationPaths.TranscodingTempPath, "*.m3u8")
.Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1) .Where(i => i.IndexOf(normalizedPlaylistId, StringComparison.OrdinalIgnoreCase) != -1)
@ -100,15 +89,20 @@ namespace MediaBrowser.Api.Playback.Hls
{ {
ExtendPlaylistTimer(playlist); ExtendPlaylistTimer(playlist);
} }
return ResultFactory.GetStaticFileResult(Request, file);
} }
private async void ExtendPlaylistTimer(string 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); await Task.Delay(20000).ConfigureAwait(false);
ApiEntryPoint.Instance.OnTranscodeEndRequest(playlist, TranscodingJobType.Hls); if (job != null)
{
ApiEntryPoint.Instance.OnTranscodeEndRequest(job);
}
} }
/// <summary> /// <summary>

View File

@ -55,15 +55,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true); return ProcessRequest(request, true);
} }
/// <summary> protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
/// 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)
{ {
var audioTranscodeParams = new List<string>(); 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}\"", return string.Format("{0} -i {1} -threads {2}{3} {4} -id3v2_version 3 -write_id3v1 1 -y \"{5}\"",
inputModifier, inputModifier,
GetInputArgument(state), GetInputArgument(transcodingJobId, state),
threads, threads,
vn, vn,
string.Join(" ", audioTranscodeParams.ToArray()), string.Join(" ", audioTranscodeParams.ToArray()),

View File

@ -142,10 +142,9 @@ namespace MediaBrowser.Api.Playback.Progressive
var outputPath = state.OutputFilePath; var outputPath = state.OutputFilePath;
var outputPathExists = File.Exists(outputPath); var outputPathExists = File.Exists(outputPath);
var isStatic = request.Static || var isTranscodeCached = outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive);
(outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive));
AddDlnaHeaders(state, responseHeaders, isStatic); AddDlnaHeaders(state, responseHeaders, request.Static || isTranscodeCached);
// Static stream // Static stream
if (request.Static) if (request.Static)
@ -154,6 +153,10 @@ namespace MediaBrowser.Api.Playback.Progressive
using (state) using (state)
{ {
var job = string.IsNullOrEmpty(request.TranscodingJobId) ?
null :
ApiEntryPoint.Instance.GetTranscodingJob(request.TranscodingJobId);
var limits = new List<long>(); var limits = new List<long>();
if (state.InputBitrate.HasValue) 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 // 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 return ResultFactory.GetStaticFileResult(Request, new StaticFileResultOptions
{ {
@ -182,17 +191,17 @@ namespace MediaBrowser.Api.Playback.Progressive
Path = state.MediaPath, Path = state.MediaPath,
Throttle = request.Throttle, Throttle = request.Throttle,
// Pad by 20% to play it safe ThrottleLimit = bytesPerSecond,
ThrottleLimit = Convert.ToInt64(1.2 * throttleLimit),
// 3.5 minutes MinThrottlePosition = targetGap,
MinThrottlePosition = throttleLimit * 210
ThrottleCallback = (l1, l2) => ThrottleCallack(l1, l2, bytesPerSecond, job)
}); });
} }
} }
// Not static but transcode cache file exists // Not static but transcode cache file exists
if (outputPathExists && !ApiEntryPoint.Instance.HasActiveTranscodingJob(outputPath, TranscodingJobType.Progressive)) if (isTranscodeCached)
{ {
var contentType = state.GetMimeType(outputPath); 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> /// <summary>
/// Gets the static remote stream result. /// Gets the static remote stream result.
/// </summary> /// </summary>
@ -325,17 +395,18 @@ namespace MediaBrowser.Api.Playback.Progressive
await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false); await ApiEntryPoint.Instance.TranscodingStartLock.WaitAsync(cancellationTokenSource.Token).ConfigureAwait(false);
try try
{ {
TranscodingJob job;
if (!File.Exists(outputPath)) if (!File.Exists(outputPath))
{ {
await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false); job = await StartFfMpeg(state, outputPath, cancellationTokenSource).ConfigureAwait(false);
} }
else else
{ {
ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive); job = ApiEntryPoint.Instance.OnTranscodeBeginRequest(outputPath, TranscodingJobType.Progressive);
state.Dispose(); state.Dispose();
} }
var job = ApiEntryPoint.Instance.GetTranscodingJob(outputPath, TranscodingJobType.Progressive);
var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job); var result = new ProgressiveStreamWriter(outputPath, Logger, FileSystem, job);
result.Options["Content-Type"] = contentType; result.Options["Content-Type"] = contentType;

View File

@ -1,7 +1,8 @@
using System; using System.Threading;
using MediaBrowser.Common.IO; using MediaBrowser.Common.IO;
using MediaBrowser.Model.Logging; using MediaBrowser.Model.Logging;
using ServiceStack.Web; using ServiceStack.Web;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
@ -73,7 +74,10 @@ namespace MediaBrowser.Api.Playback.Progressive
} }
finally 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 IFileSystem _fileSystem;
private readonly TranscodingJob _job; private readonly TranscodingJob _job;
private long _bytesWritten = 0;
public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job) public ProgressiveFileCopier(IFileSystem fileSystem, TranscodingJob job)
{ {
_fileSystem = fileSystem; _fileSystem = fileSystem;
@ -98,7 +104,7 @@ namespace MediaBrowser.Api.Playback.Progressive
{ {
while (eofCount < 15) while (eofCount < 15)
{ {
await fs.CopyToAsync(outputStream).ConfigureAwait(false); await CopyToAsyncInternal(fs, outputStream, 81920, CancellationToken.None).ConfigureAwait(false);
var fsPosition = fs.Position; 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);
}
}
}
} }
} }

View File

@ -84,14 +84,7 @@ namespace MediaBrowser.Api.Playback.Progressive
return ProcessRequest(request, true); return ProcessRequest(request, true);
} }
/// <summary> protected override string GetCommandLineArguments(string outputPath, string transcodingJobId, StreamState state, bool isEncoding)
/// 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)
{ {
// Get the output codec name // Get the output codec name
var videoCodec = state.OutputVideoCodec; 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}\"", return string.Format("{0} -i {1}{2} {3} {4} -map_metadata -1 -threads {5} {6}{7} -y \"{8}\"",
inputModifier, inputModifier,
GetInputArgument(state), GetInputArgument(transcodingJobId, state),
keyFrame, keyFrame,
GetMapArgs(state), GetMapArgs(state),
GetVideoArguments(state, videoCodec), GetVideoArguments(state, videoCodec),

View File

@ -72,6 +72,7 @@ namespace MediaBrowser.Api.Playback
public string Params { get; set; } public string Params { get; set; }
public bool Throttle { get; set; } public bool Throttle { get; set; }
public string TranscodingJobId { get; set; }
} }
public class VideoStreamRequest : StreamRequest public class VideoStreamRequest : StreamRequest

View File

@ -205,7 +205,7 @@ namespace MediaBrowser.Controller.Entities
list.Add(await GetUserView(CollectionType.MovieMovies, user, "2", parent).ConfigureAwait(false)); 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.MovieCollections, user, "3", parent).ConfigureAwait(false));
list.Add(await GetUserView(CollectionType.MovieFavorites, user, "4", 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); 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.TvLatest, user, "2", parent).ConfigureAwait(false));
list.Add(await GetUserView(CollectionType.TvSeries, user, "3", 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.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); 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.RecentlyPlayedGames, user, "1", parent).ConfigureAwait(false));
list.Add(await GetUserView(CollectionType.GameFavorites, user, "2", 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.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); return GetResult(list, query);
} }

View File

@ -363,7 +363,7 @@ xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\" /y /d /r /i
<PreBuildEvent> <PreBuildEvent>
</PreBuildEvent> </PreBuildEvent>
</PropertyGroup> </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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -21,6 +21,9 @@ namespace MediaBrowser.Controller.Net
public bool Throttle { get; set; } public bool Throttle { get; set; }
public long ThrottleLimit { get; set; } public long ThrottleLimit { get; set; }
public long MinThrottlePosition { get; set; } public long MinThrottlePosition { get; set; }
public Func<long, long, long> ThrottleCallback { get; set; }
public Action OnComplete { get; set; }
public StaticResultOptions() public StaticResultOptions()
{ {

View File

@ -22,8 +22,6 @@ namespace MediaBrowser.Dlna.Ssdp
/// </summary> /// </summary>
public int SendCount { get; private set; } public int SendCount { get; private set; }
public bool HandleBindError { get; set; }
private readonly ILogger _logger; private readonly ILogger _logger;
public Datagram(IPEndPoint toEndPoint, IPEndPoint fromEndPoint, ILogger logger, string message, int totalSendCount) public Datagram(IPEndPoint toEndPoint, IPEndPoint fromEndPoint, ILogger logger, string message, int totalSendCount)
@ -43,19 +41,9 @@ namespace MediaBrowser.Dlna.Ssdp
var client = CreateSocket(); var client = CreateSocket();
if (FromEndPoint != null) if (FromEndPoint != null)
{
try
{ {
client.Bind(FromEndPoint); client.Bind(FromEndPoint);
} }
catch
{
if (!HandleBindError)
{
throw;
}
}
}
client.BeginSendTo(msg, 0, msg.Length, SocketFlags.None, ToEndPoint, result => client.BeginSendTo(msg, 0, msg.Length, SocketFlags.None, ToEndPoint, result =>
{ {

View File

@ -124,22 +124,18 @@ namespace MediaBrowser.Dlna.Ssdp
IPEndPoint localAddress, IPEndPoint localAddress,
int sendCount = 1) int sendCount = 1)
{ {
SendDatagram(header, values, _ssdpEndp, localAddress, false, sendCount); SendDatagram(header, values, _ssdpEndp, localAddress, sendCount);
} }
public void SendDatagram(string header, public void SendDatagram(string header,
Dictionary<string, string> values, Dictionary<string, string> values,
IPEndPoint endpoint, IPEndPoint endpoint,
IPEndPoint localAddress, IPEndPoint localAddress,
bool handleBindError,
int sendCount = 1) int sendCount = 1)
{ {
var msg = new SsdpMessageBuilder().BuildMessage(header, values); var msg = new SsdpMessageBuilder().BuildMessage(header, values);
var dgram = new Datagram(endpoint, localAddress, _logger, msg, sendCount) var dgram = new Datagram(endpoint, localAddress, _logger, msg, sendCount);
{
HandleBindError = handleBindError
};
if (_messageQueue.Count == 0) if (_messageQueue.Count == 0)
{ {
@ -175,7 +171,7 @@ namespace MediaBrowser.Dlna.Ssdp
values["ST"] = d.Type; values["ST"] = d.Type;
values["USN"] = d.USN; values["USN"] = d.USN;
SendDatagram(header, values, endpoint, new IPEndPoint(d.Address, 0), true); SendDatagram(header, values, endpoint, null);
if (_config.GetDlnaConfiguration().EnableDebugLogging) if (_config.GetDlnaConfiguration().EnableDebugLogging)
{ {

View File

@ -91,7 +91,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -372,7 +372,7 @@
xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\net45\" /y /d /r /i xcopy "$(TargetPath)" "$(SolutionDir)\Nuget\dlls\net45\" /y /d /r /i
)</PostBuildEvent> )</PostBuildEvent>
</PropertyGroup> </PropertyGroup>
<Import Project="$(SolutionDir)\.nuget\NuGet.targets" Condition=" '$(ConfigurationName)' != 'Release Mono' " /> <Import Project="$(SolutionDir)\.nuget\NuGet.targets" />
<Import Project="Fody.targets" /> <Import Project="Fody.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.

View File

@ -211,7 +211,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -378,8 +378,8 @@ namespace MediaBrowser.Server.Implementations.Dto
if (album != null) if (album != null)
{ {
dto.Album = item.Name; dto.Album = album.Name;
dto.AlbumId = item.Id.ToString("N"); dto.AlbumId = album.Id.ToString("N");
} }
} }

View File

@ -102,14 +102,6 @@ namespace MediaBrowser.Server.Implementations.HttpServer
return result; return result;
} }
private bool SupportsCompression
{
get
{
return true;
}
}
/// <summary> /// <summary>
/// Gets the optimized result. /// Gets the optimized result.
/// </summary> /// </summary>
@ -127,7 +119,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
throw new ArgumentNullException("result"); throw new ArgumentNullException("result");
} }
var optimizedResult = SupportsCompression ? requestContext.ToOptimizedResult(result) : result; var optimizedResult = requestContext.ToOptimizedResult(result);
if (responseHeaders != null) if (responseHeaders != null)
{ {
@ -471,7 +463,9 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{ {
Throttle = options.Throttle, Throttle = options.Throttle,
ThrottleLimit = options.ThrottleLimit, ThrottleLimit = options.ThrottleLimit,
MinThrottlePosition = options.MinThrottlePosition MinThrottlePosition = options.MinThrottlePosition,
ThrottleCallback = options.ThrottleCallback,
OnComplete = options.OnComplete
}; };
} }
@ -488,40 +482,21 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{ {
Throttle = options.Throttle, Throttle = options.Throttle,
ThrottleLimit = options.ThrottleLimit, ThrottleLimit = options.ThrottleLimit,
MinThrottlePosition = options.MinThrottlePosition MinThrottlePosition = options.MinThrottlePosition,
ThrottleCallback = options.ThrottleCallback,
OnComplete = options.OnComplete
}; };
} }
string content; string content;
long originalContentLength = 0;
using (var stream = await factoryFn().ConfigureAwait(false)) 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); var contents = content.Compress(requestedCompressionType);

View File

@ -27,6 +27,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
public bool Throttle { get; set; } public bool Throttle { get; set; }
public long ThrottleLimit { get; set; } public long ThrottleLimit { get; set; }
public long MinThrottlePosition; public long MinThrottlePosition;
public Func<long, long, long> ThrottleCallback { get; set; }
public Action OnComplete { get; set; }
/// <summary> /// <summary>
/// The _options /// The _options
@ -167,7 +169,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{ {
responseStream = new ThrottledStream(responseStream, ThrottleLimit) responseStream = new ThrottledStream(responseStream, ThrottleLimit)
{ {
MinThrottlePosition = MinThrottlePosition MinThrottlePosition = MinThrottlePosition,
ThrottleCallback = ThrottleCallback
}; };
} }
var task = WriteToAsync(responseStream); var task = WriteToAsync(responseStream);
@ -181,6 +184,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// <param name="responseStream">The response stream.</param> /// <param name="responseStream">The response stream.</param>
/// <returns>Task.</returns> /// <returns>Task.</returns>
private async Task WriteToAsync(Stream responseStream) private async Task WriteToAsync(Stream responseStream)
{
try
{ {
// Headers only // Headers only
if (IsHeadRequest) if (IsHeadRequest)
@ -201,6 +206,14 @@ namespace MediaBrowser.Server.Implementations.HttpServer
} }
} }
} }
finally
{
if (OnComplete != null)
{
OnComplete();
}
}
}
private async Task CopyToAsyncInternal(Stream source, Stream destination, int copyLength, CancellationToken cancellationToken) private async Task CopyToAsyncInternal(Stream source, Stream destination, int copyLength, CancellationToken cancellationToken)
{ {

View File

@ -39,6 +39,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
public bool Throttle { get; set; } public bool Throttle { get; set; }
public long ThrottleLimit { get; set; } public long ThrottleLimit { get; set; }
public long MinThrottlePosition; public long MinThrottlePosition;
public Func<long, long, long> ThrottleCallback { get; set; }
public Action OnComplete { get; set; }
/// <summary> /// <summary>
/// Initializes a new instance of the <see cref="StreamWriter" /> class. /// Initializes a new instance of the <see cref="StreamWriter" /> class.
@ -85,7 +87,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
{ {
responseStream = new ThrottledStream(responseStream, ThrottleLimit) responseStream = new ThrottledStream(responseStream, ThrottleLimit)
{ {
MinThrottlePosition = MinThrottlePosition MinThrottlePosition = MinThrottlePosition,
ThrottleCallback = ThrottleCallback
}; };
} }
var task = WriteToAsync(responseStream); var task = WriteToAsync(responseStream);
@ -113,6 +116,13 @@ namespace MediaBrowser.Server.Implementations.HttpServer
throw; throw;
} }
finally
{
if (OnComplete != null)
{
OnComplete();
}
}
} }
} }
} }

View File

@ -15,6 +15,8 @@ namespace MediaBrowser.Server.Implementations.HttpServer
/// </summary> /// </summary>
public const long Infinite = 0; public const long Infinite = 0;
public Func<long, long, long> ThrottleCallback { get; set; }
#region Private members #region Private members
/// <summary> /// <summary>
/// The base stream. /// The base stream.
@ -278,6 +280,32 @@ namespace MediaBrowser.Server.Implementations.HttpServer
} }
#endregion #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 #region Protected methods
/// <summary> /// <summary>
/// Throttles for the specified buffer size in bytes. /// 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> /// <param name="bufferSizeInBytes">The buffer size in bytes.</param>
protected void Throttle(int bufferSizeInBytes) protected void Throttle(int bufferSizeInBytes)
{ {
if (_bytesWritten < MinThrottlePosition) if (!ThrottleCheck(bufferSizeInBytes))
{ {
return; return ;
}
// Make sure the buffer isn't empty.
if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
{
return;
} }
_byteCount += bufferSizeInBytes; _byteCount += bufferSizeInBytes;
@ -332,13 +354,7 @@ namespace MediaBrowser.Server.Implementations.HttpServer
protected async Task ThrottleAsync(int bufferSizeInBytes, CancellationToken cancellationToken) protected async Task ThrottleAsync(int bufferSizeInBytes, CancellationToken cancellationToken)
{ {
if (_bytesWritten < MinThrottlePosition) if (!ThrottleCheck(bufferSizeInBytes))
{
return;
}
// Make sure the buffer isn't empty.
if (_maximumBytesPerSecond <= 0 || bufferSizeInBytes <= 0)
{ {
return; return;
} }

View File

@ -501,7 +501,7 @@
</ItemGroup> </ItemGroup>
<ItemGroup /> <ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" /> <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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">

View File

@ -2136,7 +2136,7 @@
<PostBuildEvent> <PostBuildEvent>
</PostBuildEvent> </PostBuildEvent>
</PropertyGroup> </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. <!-- 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. Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild"> <Target Name="BeforeBuild">