diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index 2713059212..fc48e9a3ac 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -81,6 +81,7 @@ + diff --git a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs index 09f1ab567f..bc1c86eeee 100644 --- a/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs +++ b/MediaBrowser.Api/Playback/Progressive/BaseProgressiveStreamingService.cs @@ -197,97 +197,6 @@ 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 job = string.IsNullOrEmpty(request.TranscodingJobId) ? - //null : - //ApiEntryPoint.Instance.GetTranscodingJob(request.TranscodingJobId); - - //var limits = new List(); - //if (state.InputBitrate.HasValue) - //{ - // // Bytes per second - // limits.Add((state.InputBitrate.Value / 8)); - //} - //if (state.InputFileSize.HasValue && state.RunTimeTicks.HasValue) - //{ - // var totalSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds; - - // if (totalSeconds > 1) - // { - // var timeBasedLimit = state.InputFileSize.Value / totalSeconds; - // limits.Add(Convert.ToInt64(timeBasedLimit)); - // } - //} - - //// Take the greater of the above to methods, just to be safe - //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; - - 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; - } - /// /// Gets the static remote stream result. /// diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs new file mode 100644 index 0000000000..81848c0170 --- /dev/null +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -0,0 +1,157 @@ +using MediaBrowser.Model.Logging; +using System; +using System.IO; +using System.Threading; + +namespace MediaBrowser.Api.Playback +{ + public class TranscodingThrottler : IDisposable + { + private readonly TranscodingJob _job; + private readonly ILogger _logger; + private Timer _timer; + + public void Start() + { + _timer = new Timer(TimerCallback, null, 1000, 1000); + } + + private void TimerCallback(object state) + { + if (IsThrottleAllowed(_job)) + { + PauseTranscoding(); + } + else + { + UnpauseTranscoding(); + } + } + + private void PauseTranscoding() + { + _logger.Debug("Sending pause command to ffmpeg"); + _job.Process.StandardInput.WriteLine("p"); + } + + private void UnpauseTranscoding() + { + _logger.Debug("Sending unpause command to ffmpeg"); + _job.Process.StandardInput.WriteLine("u"); + } + + private readonly long _gapLengthInTicks = TimeSpan.FromMinutes(2).Ticks; + + public TranscodingThrottler(TranscodingJob job, ILogger logger) + { + _job = job; + _logger = logger; + } + + private bool IsThrottleAllowed(TranscodingJob job) + { + //var job = string.IsNullOrEmpty(request.TranscodingJobId) ? + //null : + //ApiEntryPoint.Instance.GetTranscodingJob(request.TranscodingJobId); + + //var limits = new List(); + //if (state.InputBitrate.HasValue) + //{ + // // Bytes per second + // limits.Add((state.InputBitrate.Value / 8)); + //} + //if (state.InputFileSize.HasValue && state.RunTimeTicks.HasValue) + //{ + // var totalSeconds = TimeSpan.FromTicks(state.RunTimeTicks.Value).TotalSeconds; + + // if (totalSeconds > 1) + // { + // var timeBasedLimit = state.InputFileSize.Value / totalSeconds; + // limits.Add(Convert.ToInt64(timeBasedLimit)); + // } + //} + + //// Take the greater of the above to methods, just to be safe + //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; + + var bytesDownloaded = job.BytesDownloaded ?? 0; + var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0; + var downloadPositionTicks = job.DownloadPositionTicks ?? 0; + + var path = job.Path; + + 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 false; + } + + //Logger.Debug("Throttling transcoder gap {0} target gap {1}", gap, targetGap); + return true; + } + + 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 false; + } + + //Logger.Debug("Throttling transcoder gap {0} target gap {1} bytes downloaded {2}", gap, targetGap, bytesDownloaded); + return true; + } + catch + { + //Logger.Error("Error getting output size"); + } + } + else + { + //Logger.Debug("No throttle data for " + path); + } + + return false; + } + + public void Dispose() + { + DisposeTimer(); + } + + private void DisposeTimer() + { + if (_timer != null) + { + _timer.Dispose(); + _timer = null; + } + } + } +} diff --git a/MediaBrowser.Model/ApiClient/ConnectionOptions.cs b/MediaBrowser.Model/ApiClient/ConnectionOptions.cs index 445eaa04ef..e12676311b 100644 --- a/MediaBrowser.Model/ApiClient/ConnectionOptions.cs +++ b/MediaBrowser.Model/ApiClient/ConnectionOptions.cs @@ -13,11 +13,17 @@ namespace MediaBrowser.Model.ApiClient /// /// true if [report capabilities]; otherwise, false. public bool ReportCapabilities { get; set; } + /// + /// Gets or sets a value indicating whether [update date last accessed]. + /// + /// true if [update date last accessed]; otherwise, false. + public bool UpdateDateLastAccessed { get; set; } public ConnectionOptions() { EnableWebSocket = true; ReportCapabilities = true; + UpdateDateLastAccessed = true; } } } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 6fa29a533b..a40e4feb39 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -267,7 +267,7 @@ namespace MediaBrowser.Model.Dlna if (subtitleStream != null) { - SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile); + SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile, options.Context); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; @@ -292,7 +292,7 @@ namespace MediaBrowser.Model.Dlna { if (subtitleStream != null) { - SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile); + SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile, options.Context); playlistItem.SubtitleDeliveryMethod = subtitleProfile.Method; playlistItem.SubtitleFormat = subtitleProfile.Format; @@ -527,7 +527,7 @@ namespace MediaBrowser.Model.Dlna { if (subtitleStream != null) { - SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile); + SubtitleProfile subtitleProfile = GetSubtitleProfile(subtitleStream, options.Profile, options.Context); if (subtitleProfile.Method != SubtitleDeliveryMethod.External && subtitleProfile.Method != SubtitleDeliveryMethod.Embed) { @@ -538,14 +538,20 @@ namespace MediaBrowser.Model.Dlna return IsAudioEligibleForDirectPlay(item, maxBitrate); } - public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, DeviceProfile deviceProfile) + public static SubtitleProfile GetSubtitleProfile(MediaStream subtitleStream, DeviceProfile deviceProfile, EncodingContext context) { // Look for an external profile that matches the stream type (text/graphical) foreach (SubtitleProfile profile in deviceProfile.SubtitleProfiles) { - if (subtitleStream.SupportsExternalStream) + if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) { - if (profile.Method == SubtitleDeliveryMethod.External && subtitleStream.IsTextSubtitleStream == MediaStream.IsTextFormat(profile.Format)) + if (subtitleStream.SupportsExternalStream) + { + return profile; + } + + // For sync we can handle the longer extraction times + if (context == EncodingContext.Static && subtitleStream.IsTextSubtitleStream) { return profile; } diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 57a3899d4d..f0854c3f85 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -262,7 +262,7 @@ namespace MediaBrowser.Model.Dlna private SubtitleStreamInfo GetSubtitleStreamInfo(MediaStream stream) { - SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile); + SubtitleProfile subtitleProfile = StreamBuilder.GetSubtitleProfile(stream, DeviceProfile, Context); if (subtitleProfile.Method != SubtitleDeliveryMethod.External) { diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index 09da16b555..6852922e44 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.577 + 3.0.578 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Media Browser Theater and Media Browser Server. Not intended for plugin developer consumption. Copyright © Media Browser 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 6841953ad3..f3053fd601 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.577 + 3.0.578 MediaBrowser.Common Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 0e3d483eb9..bb4ad23c8f 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.577 + 3.0.578 MediaBrowser.Model - Signed Edition Media Browser Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index f0883cc17f..fa6e4020f4 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.577 + 3.0.578 Media Browser.Server.Core Media Browser Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Media Browser Server. Copyright © Media Browser 2013 - +