From 5f044cfd68eecf116df1a646806fc091f3fb63aa Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 30 Mar 2015 12:16:34 -0400 Subject: [PATCH] add setting to control transcodng throttle --- .../Playback/BaseStreamingService.cs | 11 +- MediaBrowser.Api/Playback/StreamState.cs | 36 ++++ .../Playback/TranscodingThrottler.cs | 26 ++- MediaBrowser.Dlna/Didl/DidlBuilder.cs | 8 +- MediaBrowser.Dlna/PlayTo/PlayToController.cs | 4 +- .../Encoder/AudioEncoder.cs | 2 +- .../Encoder/BaseEncoder.cs | 81 ++----- .../Encoder/EncodingJob.cs | 54 ++++- .../Encoder/EncodingJobFactory.cs | 197 +++++++----------- .../Encoder/MediaEncoder.cs | 4 - .../Encoder/VideoEncoder.cs | 2 +- .../Configuration/EncodingOptions.cs | 4 + MediaBrowser.Model/Dlna/ConditionProcessor.cs | 13 +- .../Dlna/ContentFeatureBuilder.cs | 8 +- MediaBrowser.Model/Dlna/DeviceProfile.cs | 6 +- .../Dlna/ProfileConditionValue.cs | 5 +- MediaBrowser.Model/Dlna/StreamBuilder.cs | 13 +- MediaBrowser.Model/Dlna/StreamInfo.cs | 36 ++++ MediaBrowser.Model/Dto/MediaSourceInfo.cs | 34 ++- .../Localization/JavaScript/javascript.json | 3 +- .../Localization/Server/server.json | 4 +- .../Sync/CloudSyncProfile.cs | 147 ++++++++++++- .../Api/PackageCreator.cs | 2 +- .../MediaBrowser.WebDashboard.csproj | 4 +- Nuget/MediaBrowser.Common.Internal.nuspec | 4 +- Nuget/MediaBrowser.Common.nuspec | 2 +- Nuget/MediaBrowser.Model.Signed.nuspec | 2 +- Nuget/MediaBrowser.Server.Core.nuspec | 4 +- 28 files changed, 475 insertions(+), 241 deletions(-) diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index c4cdfc9ed..8c4bcf0a3 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1080,7 +1080,7 @@ namespace MediaBrowser.Api.Playback { if (state.RunTimeTicks.Value >= TimeSpan.FromMinutes(5).Ticks && state.IsInputVideo) { - transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger); + transcodingJob.TranscodingThrottler = state.TranscodingThrottler = new TranscodingThrottler(transcodingJob, Logger, ServerConfigurationManager); state.TranscodingThrottler.Start(); } } @@ -2012,7 +2012,6 @@ namespace MediaBrowser.Api.Playback } var audioCodec = state.ActualOutputAudioCodec; - var videoCodec = state.ActualOutputVideoCodec; var mediaProfile = state.VideoRequest == null ? @@ -2033,7 +2032,9 @@ namespace MediaBrowser.Api.Playback state.TargetTimestamp, state.IsTargetAnamorphic, state.IsTargetCabac, - state.TargetRefFrames); + state.TargetRefFrames, + state.TargetVideoStreamCount, + state.TargetAudioStreamCount); if (mediaProfile != null) { @@ -2118,7 +2119,9 @@ namespace MediaBrowser.Api.Playback state.TranscodeSeekInfo, state.IsTargetAnamorphic, state.IsTargetCabac, - state.TargetRefFrames + state.TargetRefFrames, + state.TargetVideoStreamCount, + state.TargetAudioStreamCount ).FirstOrDefault() ?? string.Empty; } diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index b097f3b6a..2d1e896db 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -346,6 +346,42 @@ namespace MediaBrowser.Api.Playback } } + public int? TargetVideoStreamCount + { + get + { + if (Request.Static) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (Request.Static) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + /// /// Predicts the audio sample rate that will be in the output stream /// diff --git a/MediaBrowser.Api/Playback/TranscodingThrottler.cs b/MediaBrowser.Api/Playback/TranscodingThrottler.cs index 19a9fe934..58cfa086e 100644 --- a/MediaBrowser.Api/Playback/TranscodingThrottler.cs +++ b/MediaBrowser.Api/Playback/TranscodingThrottler.cs @@ -1,4 +1,6 @@ -using MediaBrowser.Model.Logging; +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using MediaBrowser.Model.Logging; using System; using System.IO; using System.Threading; @@ -11,13 +13,18 @@ namespace MediaBrowser.Api.Playback private readonly ILogger _logger; private Timer _timer; private bool _isPaused; + private readonly IConfigurationManager _config; - private readonly long _gapLengthInTicks = TimeSpan.FromMinutes(2).Ticks; - - public TranscodingThrottler(TranscodingJob job, ILogger logger) + public TranscodingThrottler(TranscodingJob job, ILogger logger, IConfigurationManager config) { _job = job; _logger = logger; + _config = config; + } + + private EncodingOptions GetOptions() + { + return _config.GetConfiguration("encoding"); } public void Start() @@ -33,7 +40,9 @@ namespace MediaBrowser.Api.Playback return; } - if (IsThrottleAllowed(_job)) + var options = GetOptions(); + + if (options.EnableThrottling && IsThrottleAllowed(_job, options.ThrottleThresholdSeconds)) { PauseTranscoding(); } @@ -79,19 +88,20 @@ namespace MediaBrowser.Api.Playback } } - private bool IsThrottleAllowed(TranscodingJob job) + private bool IsThrottleAllowed(TranscodingJob job, int thresholdSeconds) { var bytesDownloaded = job.BytesDownloaded ?? 0; var transcodingPositionTicks = job.TranscodingPositionTicks ?? 0; var downloadPositionTicks = job.DownloadPositionTicks ?? 0; var path = job.Path; + var gapLengthInTicks = TimeSpan.FromSeconds(thresholdSeconds).Ticks; if (downloadPositionTicks > 0 && transcodingPositionTicks > 0) { // HLS - time-based consideration - var targetGap = _gapLengthInTicks; + var targetGap = gapLengthInTicks; var gap = transcodingPositionTicks - downloadPositionTicks; if (gap < targetGap) @@ -113,7 +123,7 @@ namespace MediaBrowser.Api.Playback var bytesTranscoded = job.BytesTranscoded ?? new FileInfo(path).Length; // Estimate the bytes the transcoder should be ahead - double gapFactor = _gapLengthInTicks; + double gapFactor = gapLengthInTicks; gapFactor /= transcodingPositionTicks; var targetGap = bytesTranscoded * gapFactor; diff --git a/MediaBrowser.Dlna/Didl/DidlBuilder.cs b/MediaBrowser.Dlna/Didl/DidlBuilder.cs index 469b60a9c..7f696f300 100644 --- a/MediaBrowser.Dlna/Didl/DidlBuilder.cs +++ b/MediaBrowser.Dlna/Didl/DidlBuilder.cs @@ -158,7 +158,9 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TranscodeSeekInfo, streamInfo.IsTargetAnamorphic, streamInfo.IsTargetCabac, - streamInfo.TargetRefFrames); + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount); foreach (var contentFeature in contentFeatureList) { @@ -280,7 +282,9 @@ namespace MediaBrowser.Dlna.Didl streamInfo.TargetTimestamp, streamInfo.IsTargetAnamorphic, streamInfo.IsTargetCabac, - streamInfo.TargetRefFrames); + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount); var filename = url.Substring(0, url.IndexOf('?')); diff --git a/MediaBrowser.Dlna/PlayTo/PlayToController.cs b/MediaBrowser.Dlna/PlayTo/PlayToController.cs index 17385bda6..3eb091a19 100644 --- a/MediaBrowser.Dlna/PlayTo/PlayToController.cs +++ b/MediaBrowser.Dlna/PlayTo/PlayToController.cs @@ -526,7 +526,9 @@ namespace MediaBrowser.Dlna.PlayTo streamInfo.TranscodeSeekInfo, streamInfo.IsTargetAnamorphic, streamInfo.IsTargetCabac, - streamInfo.TargetRefFrames); + streamInfo.TargetRefFrames, + streamInfo.TargetVideoStreamCount, + streamInfo.TargetAudioStreamCount); return list.FirstOrDefault(); } diff --git a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs index a6a87a3fc..e9204ef5b 100644 --- a/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs @@ -14,7 +14,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class AudioEncoder : BaseEncoder { - public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager) + public AudioEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) { } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 44e0d1517..8266f4d3a 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -1,6 +1,5 @@ using MediaBrowser.Common.Configuration; using MediaBrowser.Common.IO; -using MediaBrowser.Controller.Channels; using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; @@ -14,6 +13,7 @@ using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.MediaInfo; using System; using System.Collections.Generic; using System.Diagnostics; @@ -31,10 +31,8 @@ namespace MediaBrowser.MediaEncoding.Encoder protected readonly ILogger Logger; protected readonly IServerConfigurationManager ConfigurationManager; protected readonly IFileSystem FileSystem; - protected readonly ILiveTvManager LiveTvManager; protected readonly IIsoManager IsoManager; protected readonly ILibraryManager LibraryManager; - protected readonly IChannelManager ChannelManager; protected readonly ISessionManager SessionManager; protected readonly ISubtitleEncoder SubtitleEncoder; protected readonly IMediaSourceManager MediaSourceManager; @@ -45,20 +43,18 @@ namespace MediaBrowser.MediaEncoding.Encoder ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, - ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, - IChannelManager channelManager, - ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) + ISessionManager sessionManager, + ISubtitleEncoder subtitleEncoder, + IMediaSourceManager mediaSourceManager) { MediaEncoder = mediaEncoder; Logger = logger; ConfigurationManager = configurationManager; FileSystem = fileSystem; - LiveTvManager = liveTvManager; IsoManager = isoManager; LibraryManager = libraryManager; - ChannelManager = channelManager; SessionManager = sessionManager; SubtitleEncoder = subtitleEncoder; MediaSourceManager = mediaSourceManager; @@ -68,7 +64,7 @@ namespace MediaBrowser.MediaEncoding.Encoder IProgress progress, CancellationToken cancellationToken) { - var encodingJob = await new EncodingJobFactory(Logger, LiveTvManager, LibraryManager, ChannelManager, MediaSourceManager) + var encodingJob = await new EncodingJobFactory(Logger, LibraryManager, MediaSourceManager) .CreateJob(options, IsVideoEncoder, progress, cancellationToken).ConfigureAwait(false); encodingJob.OutputFilePath = GetOutputFilePath(encodingJob); @@ -477,53 +473,25 @@ namespace MediaBrowser.MediaEncoding.Encoder state.IsoMount = await IsoManager.Mount(state.MediaPath, cancellationToken).ConfigureAwait(false); } - if (string.IsNullOrEmpty(state.MediaPath)) + if (state.MediaSource.RequiresOpening) { - var checkCodecs = false; - - if (string.Equals(state.ItemType, typeof(LiveTvChannel).Name)) + var liveStreamResponse = await MediaSourceManager.OpenLiveStream(new LiveStreamRequest { - var streamInfo = await LiveTvManager.GetChannelStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); + OpenToken = state.MediaSource.OpenToken - state.LiveTvStreamId = streamInfo.Id; + }, false, cancellationToken).ConfigureAwait(false); - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; + AttachMediaStreamInfo(state, liveStreamResponse.MediaSource, state.Options); - await Task.Delay(1500, cancellationToken).ConfigureAwait(false); - - AttachMediaStreamInfo(state, streamInfo, state.Options); - checkCodecs = true; - } - - else if (string.Equals(state.ItemType, typeof(LiveTvVideoRecording).Name) || - string.Equals(state.ItemType, typeof(LiveTvAudioRecording).Name)) + if (state.IsVideoRequest) { - var streamInfo = await LiveTvManager.GetRecordingStream(state.Options.ItemId, cancellationToken).ConfigureAwait(false); - - state.LiveTvStreamId = streamInfo.Id; - - state.MediaPath = streamInfo.Path; - state.InputProtocol = streamInfo.Protocol; - - await Task.Delay(1500, cancellationToken).ConfigureAwait(false); - - AttachMediaStreamInfo(state, streamInfo, state.Options); - checkCodecs = true; + EncodingJobFactory.TryStreamCopy(state, state.Options); } + } - if (state.IsVideoRequest && checkCodecs) - { - if (state.VideoStream != null && EncodingJobFactory.CanStreamCopyVideo(state.Options, state.VideoStream)) - { - state.OutputVideoCodec = "copy"; - } - - if (state.AudioStream != null && EncodingJobFactory.CanStreamCopyAudio(state.Options, state.AudioStream, state.SupportedAudioCodecs)) - { - state.OutputAudioCodec = "copy"; - } - } + if (state.MediaSource.BufferMs.HasValue) + { + await Task.Delay(state.MediaSource.BufferMs.Value, cancellationToken).ConfigureAwait(false); } } @@ -531,22 +499,7 @@ namespace MediaBrowser.MediaEncoding.Encoder MediaSourceInfo mediaSource, EncodingJobOptions videoRequest) { - state.InputProtocol = mediaSource.Protocol; - state.MediaPath = mediaSource.Path; - state.RunTimeTicks = mediaSource.RunTimeTicks; - state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; - state.InputBitrate = mediaSource.Bitrate; - state.InputFileSize = mediaSource.Size; - state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - - if (state.ReadInputAtNativeFramerate) - { - state.OutputAudioSync = "1000"; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - } - - EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource.MediaStreams, videoRequest); + EncodingJobFactory.AttachMediaStreamInfo(state, mediaSource, videoRequest); } /// diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs index c8d121eea..767f3f829 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs @@ -1,7 +1,8 @@ -using MediaBrowser.Controller.LiveTv; +using MediaBrowser.Controller.Library; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Drawing; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; @@ -26,7 +27,7 @@ namespace MediaBrowser.MediaEncoding.Encoder public EncodingJobOptions Options { get; set; } public string InputContainer { get; set; } - public List AllMediaStreams { get; set; } + public MediaSourceInfo MediaSource { get; set; } public MediaStream AudioStream { get; set; } public MediaStream VideoStream { get; set; } public MediaStream SubtitleStream { get; set; } @@ -76,12 +77,12 @@ namespace MediaBrowser.MediaEncoding.Encoder } private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; + private readonly IMediaSourceManager _mediaSourceManager; - public EncodingJob(ILogger logger, ILiveTvManager liveTvManager) + public EncodingJob(ILogger logger, IMediaSourceManager mediaSourceManager) { _logger = logger; - _liveTvManager = liveTvManager; + _mediaSourceManager = mediaSourceManager; Id = Guid.NewGuid().ToString("N"); RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); @@ -89,7 +90,6 @@ namespace MediaBrowser.MediaEncoding.Encoder SupportedAudioCodecs = new List(); PlayableStreamFileNames = new List(); RemoteHttpHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); - AllMediaStreams = new List(); TaskCompletionSource = new TaskCompletionSource(); } @@ -136,15 +136,15 @@ namespace MediaBrowser.MediaEncoding.Encoder private async void DisposeLiveStream() { - if (!string.IsNullOrEmpty(LiveTvStreamId)) + if (MediaSource.RequiresClosing) { try { - await _liveTvManager.CloseLiveStream(LiveTvStreamId, CancellationToken.None).ConfigureAwait(false); + await _mediaSourceManager.CloseLiveStream(MediaSource.LiveStreamId, CancellationToken.None).ConfigureAwait(false); } catch (Exception ex) { - _logger.ErrorException("Error closing live tv stream", ex); + _logger.ErrorException("Error closing media source", ex); } } } @@ -394,6 +394,42 @@ namespace MediaBrowser.MediaEncoding.Encoder } } + public int? TargetVideoStreamCount + { + get + { + if (Options.Static) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (Options.Static) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + public void ReportTranscodingProgress(TimeSpan? transcodingPosition, float? framerate, double? percentComplete, long? bytesTranscoded) { var ticks = transcodingPosition.HasValue ? transcodingPosition.Value.Ticks : (long?)null; diff --git a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs index c5783e188..8d8201074 100644 --- a/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs +++ b/MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs @@ -1,9 +1,10 @@ -using MediaBrowser.Controller.Channels; +using System.IO; using MediaBrowser.Controller.Entities; using MediaBrowser.Controller.Library; using MediaBrowser.Controller.LiveTv; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Dlna; +using MediaBrowser.Model.Dto; using MediaBrowser.Model.Entities; using MediaBrowser.Model.Logging; using MediaBrowser.Model.MediaInfo; @@ -19,19 +20,15 @@ namespace MediaBrowser.MediaEncoding.Encoder public class EncodingJobFactory { private readonly ILogger _logger; - private readonly ILiveTvManager _liveTvManager; private readonly ILibraryManager _libraryManager; - private readonly IChannelManager _channelManager; private readonly IMediaSourceManager _mediaSourceManager; protected static readonly CultureInfo UsCulture = new CultureInfo("en-US"); - public EncodingJobFactory(ILogger logger, ILiveTvManager liveTvManager, ILibraryManager libraryManager, IChannelManager channelManager, IMediaSourceManager mediaSourceManager) + public EncodingJobFactory(ILogger logger, ILibraryManager libraryManager, IMediaSourceManager mediaSourceManager) { _logger = logger; - _liveTvManager = liveTvManager; _libraryManager = libraryManager; - _channelManager = channelManager; _mediaSourceManager = mediaSourceManager; } @@ -42,9 +39,9 @@ namespace MediaBrowser.MediaEncoding.Encoder if (string.IsNullOrEmpty(request.AudioCodec)) { request.AudioCodec = InferAudioCodec(request.OutputContainer); - } - - var state = new EncodingJob(_logger, _liveTvManager) + } + + var state = new EncodingJob(_logger, _mediaSourceManager) { Options = options, IsVideoRequest = isVideoRequest, @@ -58,106 +55,17 @@ namespace MediaBrowser.MediaEncoding.Encoder } var item = _libraryManager.GetItemById(request.ItemId); - - List mediaStreams = null; - state.ItemType = item.GetType().Name; - if (item is ILiveTvRecording) - { - var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false); + state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - state.VideoType = VideoType.VideoFile; - state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); + var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); - var path = recording.RecordingInfo.Path; - var mediaUrl = recording.RecordingInfo.Url; - - var source = string.IsNullOrEmpty(request.MediaSourceId) - ? recording.GetMediaSources(false).First() - : _mediaSourceManager.GetStaticMediaSource(recording, request.MediaSourceId, false); + var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) + ? mediaSources.First() + : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); - mediaStreams = source.MediaStreams; - - // Just to prevent this from being null and causing other methods to fail - state.MediaPath = string.Empty; - - if (!string.IsNullOrEmpty(path)) - { - state.MediaPath = path; - state.InputProtocol = MediaProtocol.File; - } - else if (!string.IsNullOrEmpty(mediaUrl)) - { - state.MediaPath = mediaUrl; - state.InputProtocol = MediaProtocol.Http; - } - - state.RunTimeTicks = recording.RunTimeTicks; - state.DeInterlace = true; - state.OutputAudioSync = "1000"; - state.InputVideoSync = "-1"; - state.InputAudioSync = "1"; - state.InputContainer = recording.Container; - state.ReadInputAtNativeFramerate = source.ReadAtNativeFramerate; - } - else if (item is LiveTvChannel) - { - var channel = _liveTvManager.GetInternalChannel(request.ItemId); - - state.VideoType = VideoType.VideoFile; - state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - mediaStreams = new List(); - - state.DeInterlace = true; - - // Just to prevent this from being null and causing other methods to fail - state.MediaPath = string.Empty; - } - else - { - var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false); - - var mediaSource = string.IsNullOrEmpty(request.MediaSourceId) - ? mediaSources.First() - : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId)); - - mediaStreams = mediaSource.MediaStreams; - - state.MediaPath = mediaSource.Path; - state.InputProtocol = mediaSource.Protocol; - state.InputContainer = mediaSource.Container; - state.InputFileSize = mediaSource.Size; - state.InputBitrate = mediaSource.Bitrate; - state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; - state.RunTimeTicks = mediaSource.RunTimeTicks; - state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; - - var video = item as Video; - - if (video != null) - { - state.IsInputVideo = true; - - if (mediaSource.VideoType.HasValue) - { - state.VideoType = mediaSource.VideoType.Value; - } - - state.IsoType = mediaSource.IsoType; - - state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); - - if (mediaSource.Timestamp.HasValue) - { - state.InputTimestamp = mediaSource.Timestamp.Value; - } - } - - state.RunTimeTicks = mediaSource.RunTimeTicks; - } - - AttachMediaStreamInfo(state, mediaStreams, request); + AttachMediaStreamInfo(state, mediaSource, options); state.OutputAudioBitrate = GetAudioBitrateParam(request, state.AudioStream); state.OutputAudioSampleRate = request.AudioSampleRate; @@ -185,26 +93,73 @@ namespace MediaBrowser.MediaEncoding.Encoder ApplyDeviceProfileSettings(state); - if (isVideoRequest) - { - if (state.VideoStream != null && CanStreamCopyVideo(request, state.VideoStream)) - { - state.OutputVideoCodec = "copy"; - } - - if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs)) - { - state.OutputAudioCodec = "copy"; - } - } + TryStreamCopy(state, request); return state; } - internal static void AttachMediaStreamInfo(EncodingJob state, - List mediaStreams, + internal static void TryStreamCopy(EncodingJob state, EncodingJobOptions videoRequest) { + if (state.IsVideoRequest) + { + if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream)) + { + state.OutputVideoCodec = "copy"; + } + + if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs)) + { + state.OutputAudioCodec = "copy"; + } + } + } + + internal static void AttachMediaStreamInfo(EncodingJob state, + MediaSourceInfo mediaSource, + EncodingJobOptions videoRequest) + { + state.MediaPath = mediaSource.Path; + state.InputProtocol = mediaSource.Protocol; + state.InputContainer = mediaSource.Container; + state.InputFileSize = mediaSource.Size; + state.InputBitrate = mediaSource.Bitrate; + state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; + state.RunTimeTicks = mediaSource.RunTimeTicks; + state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; + + if (mediaSource.VideoType.HasValue) + { + state.VideoType = mediaSource.VideoType.Value; + } + + state.IsoType = mediaSource.IsoType; + + state.PlayableStreamFileNames = mediaSource.PlayableStreamFileNames.ToList(); + + if (mediaSource.Timestamp.HasValue) + { + state.InputTimestamp = mediaSource.Timestamp.Value; + } + + state.InputProtocol = mediaSource.Protocol; + state.MediaPath = mediaSource.Path; + state.RunTimeTicks = mediaSource.RunTimeTicks; + state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; + state.InputBitrate = mediaSource.Bitrate; + state.InputFileSize = mediaSource.Size; + state.ReadInputAtNativeFramerate = mediaSource.ReadAtNativeFramerate; + + if (state.ReadInputAtNativeFramerate || + mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) + { + state.OutputAudioSync = "1000"; + state.InputVideoSync = "-1"; + state.InputAudioSync = "1"; + } + + var mediaStreams = mediaSource.MediaStreams; + if (videoRequest != null) { if (string.IsNullOrEmpty(videoRequest.VideoCodec)) @@ -233,7 +188,7 @@ namespace MediaBrowser.MediaEncoding.Encoder state.AudioStream = GetMediaStream(mediaStreams, null, MediaStreamType.Audio, true); } - state.AllMediaStreams = mediaStreams; + state.MediaSource = mediaSource; } /// @@ -771,7 +726,9 @@ namespace MediaBrowser.MediaEncoding.Encoder state.TargetTimestamp, state.IsTargetAnamorphic, state.IsTargetCabac, - state.TargetRefFrames); + state.TargetRefFrames, + state.TargetVideoStreamCount, + state.TargetAudioStreamCount); if (mediaProfile != null) { diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index 7fd91bf6f..425889807 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -577,10 +577,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger, ConfigurationManager, FileSystem, - LiveTvManager, IsoManager, LibraryManager, - ChannelManager, SessionManager, SubtitleEncoder(), MediaSourceManager()) @@ -599,10 +597,8 @@ namespace MediaBrowser.MediaEncoding.Encoder _logger, ConfigurationManager, FileSystem, - LiveTvManager, IsoManager, LibraryManager, - ChannelManager, SessionManager, SubtitleEncoder(), MediaSourceManager()) diff --git a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs index b8ed97b1f..26d4a7650 100644 --- a/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs @@ -15,7 +15,7 @@ namespace MediaBrowser.MediaEncoding.Encoder { public class VideoEncoder : BaseEncoder { - public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, ILiveTvManager liveTvManager, IIsoManager isoManager, ILibraryManager libraryManager, IChannelManager channelManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, liveTvManager, isoManager, libraryManager, channelManager, sessionManager, subtitleEncoder, mediaSourceManager) + public VideoEncoder(MediaEncoder mediaEncoder, ILogger logger, IServerConfigurationManager configurationManager, IFileSystem fileSystem, IIsoManager isoManager, ILibraryManager libraryManager, ISessionManager sessionManager, ISubtitleEncoder subtitleEncoder, IMediaSourceManager mediaSourceManager) : base(mediaEncoder, logger, configurationManager, fileSystem, isoManager, libraryManager, sessionManager, subtitleEncoder, mediaSourceManager) { } diff --git a/MediaBrowser.Model/Configuration/EncodingOptions.cs b/MediaBrowser.Model/Configuration/EncodingOptions.cs index f24367298..afd67eb15 100644 --- a/MediaBrowser.Model/Configuration/EncodingOptions.cs +++ b/MediaBrowser.Model/Configuration/EncodingOptions.cs @@ -8,12 +8,16 @@ namespace MediaBrowser.Model.Configuration public double DownMixAudioBoost { get; set; } public string H264Encoder { get; set; } public bool EnableDebugLogging { get; set; } + public bool EnableThrottling { get; set; } + public int ThrottleThresholdSeconds { get; set; } public EncodingOptions() { H264Encoder = "libx264"; DownMixAudioBoost = 2; EncodingQuality = EncodingQuality.Auto; + EnableThrottling = true; + ThrottleThresholdSeconds = 120; } } } diff --git a/MediaBrowser.Model/Dlna/ConditionProcessor.cs b/MediaBrowser.Model/Dlna/ConditionProcessor.cs index e0a8e239e..376963444 100644 --- a/MediaBrowser.Model/Dlna/ConditionProcessor.cs +++ b/MediaBrowser.Model/Dlna/ConditionProcessor.cs @@ -20,7 +20,9 @@ namespace MediaBrowser.Model.Dlna TransportStreamTimestamp? timestamp, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { switch (condition.Property) { @@ -56,6 +58,10 @@ namespace MediaBrowser.Model.Dlna return IsConditionSatisfied(condition, width); case ProfileConditionValue.RefFrames: return IsConditionSatisfied(condition, refFrames); + case ProfileConditionValue.NumAudioStreams: + return IsConditionSatisfied(condition, numAudioStreams); + case ProfileConditionValue.NumVideoStreams: + return IsConditionSatisfied(condition, numVideoStreams); case ProfileConditionValue.VideoTimestamp: return IsConditionSatisfied(condition, timestamp); default: @@ -92,7 +98,8 @@ namespace MediaBrowser.Model.Dlna public bool IsVideoAudioConditionSatisfied(ProfileCondition condition, int? audioChannels, int? audioBitrate, - string audioProfile) + string audioProfile, + bool? isSecondaryTrack) { switch (condition.Property) { @@ -102,6 +109,8 @@ namespace MediaBrowser.Model.Dlna return IsConditionSatisfied(condition, audioBitrate); case ProfileConditionValue.AudioChannels: return IsConditionSatisfied(condition, audioChannels); + case ProfileConditionValue.IsSecondaryAudio: + return IsConditionSatisfied(condition, isSecondaryTrack); default: throw new ArgumentException("Unexpected condition on audio file: " + condition.Property); } diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs index a3eeecff2..8161f1c26 100644 --- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs +++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs @@ -117,7 +117,9 @@ namespace MediaBrowser.Model.Dlna TranscodeSeekInfo transcodeSeekInfo, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { // first bit means Time based seek supported, second byte range seek supported (not sure about the order now), so 01 = only byte seek, 10 = time based, 11 = both, 00 = none string orgOp = ";DLNA.ORG_OP=" + DlnaMaps.GetOrgOpValue(runtimeTicks.HasValue, isDirectStream, transcodeSeekInfo); @@ -158,7 +160,9 @@ namespace MediaBrowser.Model.Dlna timestamp, isAnamorphic, isCabac, - refFrames); + refFrames, + numVideoStreams, + numAudioStreams); List orgPnValues = new List(); diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 4b137a268..8b9b2edf3 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -281,7 +281,9 @@ namespace MediaBrowser.Model.Dlna TransportStreamTimestamp timestamp, bool? isAnamorphic, bool? isCabac, - int? refFrames) + int? refFrames, + int? numVideoStreams, + int? numAudioStreams) { container = StringHelper.TrimStart((container ?? string.Empty), '.'); @@ -315,7 +317,7 @@ namespace MediaBrowser.Model.Dlna var anyOff = false; foreach (ProfileCondition c in i.Conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(c, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { anyOff = true; break; diff --git a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs index ae6dc74c8..7563ffb5a 100644 --- a/MediaBrowser.Model/Dlna/ProfileConditionValue.cs +++ b/MediaBrowser.Model/Dlna/ProfileConditionValue.cs @@ -17,6 +17,9 @@ VideoTimestamp = 12, IsAnamorphic = 13, RefFrames = 14, - IsCabac = 15 + IsCabac = 15, + NumAudioStreams = 16, + NumVideoStreams = 17, + IsSecondaryAudio } } \ No newline at end of file diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 6534eda10..1cc37de57 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -495,10 +495,13 @@ namespace MediaBrowser.Model.Dlna int? packetLength = videoStream == null ? null : videoStream.PacketLength; int? refFrames = videoStream == null ? null : videoStream.RefFrames; + int? numAudioStreams = mediaSource.GetStreamCount(MediaStreamType.Audio); + int? numVideoStreams = mediaSource.GetStreamCount(MediaStreamType.Video); + // Check container conditions foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { return null; } @@ -525,7 +528,7 @@ namespace MediaBrowser.Model.Dlna foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames)) + if (!conditionProcessor.IsVideoConditionSatisfied(i, audioBitrate, audioChannels, width, height, bitDepth, videoBitrate, videoProfile, videoLevel, videoFramerate, packetLength, timestamp, isAnamorphic, isCabac, refFrames, numVideoStreams, numAudioStreams)) { return null; } @@ -554,7 +557,8 @@ namespace MediaBrowser.Model.Dlna foreach (ProfileCondition i in conditions) { - if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile)) + bool? isSecondaryAudio = audioStream == null ? null : mediaSource.IsSecondaryAudio(audioStream); + if (!conditionProcessor.IsVideoAudioConditionSatisfied(i, audioChannels, audioBitrate, audioProfile, isSecondaryAudio)) { return null; } @@ -752,6 +756,9 @@ namespace MediaBrowser.Model.Dlna case ProfileConditionValue.AudioProfile: case ProfileConditionValue.Has64BitOffsets: case ProfileConditionValue.PacketLength: + case ProfileConditionValue.NumAudioStreams: + case ProfileConditionValue.NumVideoStreams: + case ProfileConditionValue.IsSecondaryAudio: case ProfileConditionValue.VideoTimestamp: { // Not supported yet diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 12319a122..feee2d765 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -672,6 +672,42 @@ namespace MediaBrowser.Model.Dlna } } + public int? TargetVideoStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Video, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Video, 1); + } + } + + public int? TargetAudioStreamCount + { + get + { + if (IsDirectStream) + { + return GetMediaStreamCount(MediaStreamType.Audio, int.MaxValue); + } + return GetMediaStreamCount(MediaStreamType.Audio, 1); + } + } + + private int? GetMediaStreamCount(MediaStreamType type, int limit) + { + var count = MediaSource.GetStreamCount(type); + + if (count.HasValue) + { + count = Math.Min(count.Value, limit); + } + + return count; + } + public List GetSelectableAudioStreams() { return GetSelectableStreams(MediaStreamType.Audio); diff --git a/MediaBrowser.Model/Dto/MediaSourceInfo.cs b/MediaBrowser.Model/Dto/MediaSourceInfo.cs index 3b4513724..302d18bc0 100644 --- a/MediaBrowser.Model/Dto/MediaSourceInfo.cs +++ b/MediaBrowser.Model/Dto/MediaSourceInfo.cs @@ -46,8 +46,8 @@ namespace MediaBrowser.Model.Dto public int? Bitrate { get; set; } public TransportStreamTimestamp? Timestamp { get; set; } - public Dictionary RequiredHttpHeaders { get; set; } - + public Dictionary RequiredHttpHeaders { get; set; } + public string TranscodingUrl { get; set; } public string TranscodingSubProtocol { get; set; } public string TranscodingContainer { get; set; } @@ -135,5 +135,35 @@ namespace MediaBrowser.Model.Dto return null; } + + public int? GetStreamCount(MediaStreamType type) + { + int numMatches = 0; + int numStreams = 0; + + foreach (MediaStream i in MediaStreams) + { + numStreams++; + if (i.Type == type) + { + numMatches++; + } + } + + return numStreams == 0 ? (int?)null : numMatches; + } + + public bool? IsSecondaryAudio(MediaStream stream) + { + foreach (MediaStream currentStream in MediaStreams) + { + if (currentStream.Type == MediaStreamType.Audio) + { + return currentStream.Index != stream.Index; + } + } + + return null; + } } } diff --git a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json index fdcc4ceb6..ae20fafba 100644 --- a/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json +++ b/MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json @@ -35,6 +35,8 @@ "HeaderConfirmation": "Confirmation", "MessageKeyUpdated": "Thank you. Your supporter key has been updated.", "MessageKeyRemoved": "Thank you. Your supporter key has been removed.", + "TitleLiveTV": "Live TV", + "TitleSync": "Sync", "ErrorLaunchingChromecast": "There was an error launching chromecast. Please ensure your device is connected to your wireless network.", "MessageErrorLoadingSupporterInfo": "There was an error loading supporter information. Please try again later.", "MessageLinkYourSupporterKey": "Link your supporter key with up to {0} Emby Connect members to enjoy free access to the following apps:", @@ -93,7 +95,6 @@ "HeaderWelcomeToProjectWebClient": "Welcome to the Emby Web Client", "ButtonTakeTheTour": "Take the tour", "HeaderWelcomeBack": "Welcome back!", - "TitleSync": "Sync", "TitlePlugins": "Plugins", "ButtonTakeTheTourToSeeWhatsNew": "Take the tour to see what's new", "MessageNoSyncJobsFound": "No sync jobs found. Create sync jobs using the Sync buttons found throughout the web interface.", diff --git a/MediaBrowser.Server.Implementations/Localization/Server/server.json b/MediaBrowser.Server.Implementations/Localization/Server/server.json index 0fc603a08..f52d8a9fe 100644 --- a/MediaBrowser.Server.Implementations/Localization/Server/server.json +++ b/MediaBrowser.Server.Implementations/Localization/Server/server.json @@ -1400,5 +1400,7 @@ "HeaderUpcomingPrograms": "Upcoming Programs", "ButtonMoreItems": "More...", "LabelShowLibraryTileNames": "Show library tile names", - "LabelShowLibraryTileNamesHelp": "Determines if labels will be displayed underneath library tiles on the home page" + "LabelShowLibraryTileNamesHelp": "Determines if labels will be displayed underneath library tiles on the home page", + "OptionEnableTranscodingThrottle": "Enable throttling", + "OptionEnableTranscodingThrottleHelp": "Throttling will automatically adjust transcoding speed in order to minimize server cpu utilization during playback." } diff --git a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs index 8d4c40fdc..c3e8cf944 100644 --- a/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs +++ b/MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs @@ -1,4 +1,5 @@ -using MediaBrowser.Model.Dlna; +using System.Collections.Generic; +using MediaBrowser.Model.Dlna; namespace MediaBrowser.Server.Implementations.Sync { @@ -25,6 +26,9 @@ namespace MediaBrowser.Server.Implementations.Sync mkvAudio += ",dca"; } + var videoProfile = "high|main|baseline|constrained baseline"; + var videoLevel = "41"; + DirectPlayProfiles = new[] { new DirectPlayProfile @@ -48,13 +52,37 @@ namespace MediaBrowser.Server.Implementations.Sync } }; - ContainerProfiles = new ContainerProfile[] { }; + ContainerProfiles = new[] + { + new ContainerProfile + { + Type = DlnaProfileType.Video, + Conditions = new [] + { + new ProfileCondition + { + Condition = ProfileConditionType.NotEquals, + Property = ProfileConditionValue.NumAudioStreams, + Value = "0", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.EqualsAny, + Property = ProfileConditionValue.NumVideoStreams, + Value = "1", + IsRequired = false + } + } + } + }; - CodecProfiles = new[] + var codecProfiles = new List { new CodecProfile { Type = CodecType.Video, + Codec = "h264", Conditions = new [] { new ProfileCondition @@ -65,11 +93,18 @@ namespace MediaBrowser.Server.Implementations.Sync IsRequired = false }, new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.Width, + Value = "1920", + IsRequired = true + }, + new ProfileCondition { Condition = ProfileConditionType.LessThanEqual, Property = ProfileConditionValue.Height, Value = "1080", - IsRequired = false + IsRequired = true }, new ProfileCondition { @@ -77,11 +112,115 @@ namespace MediaBrowser.Server.Implementations.Sync Property = ProfileConditionValue.RefFrames, Value = "4", IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoFramerate, + Value = "30", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsAnamorphic, + Value = "false", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoLevel, + Value = videoLevel, + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.EqualsAny, + Property = ProfileConditionValue.VideoProfile, + Value = videoProfile, + IsRequired = false + } + } + }, + new CodecProfile + { + Type = CodecType.Video, + Codec = "mpeg4", + Conditions = new [] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoBitDepth, + Value = "8", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.Width, + Value = "1920", + IsRequired = true + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.Height, + Value = "1080", + IsRequired = true + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.RefFrames, + Value = "4", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.VideoFramerate, + Value = "30", + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsAnamorphic, + Value = "false", + IsRequired = false } } } }; + var maxAudioChannels = supportsAc3 || supportsDca ? "5" : "2"; + codecProfiles.Add(new CodecProfile + { + Type = CodecType.Audio, + Codec = "mpeg4", + Conditions = new[] + { + new ProfileCondition + { + Condition = ProfileConditionType.LessThanEqual, + Property = ProfileConditionValue.AudioChannels, + Value = maxAudioChannels, + IsRequired = false + }, + new ProfileCondition + { + Condition = ProfileConditionType.Equals, + Property = ProfileConditionValue.IsSecondaryAudio, + Value = "false", + IsRequired = false + } + } + }); + + CodecProfiles = codecProfiles.ToArray(); + SubtitleProfiles = new[] { new SubtitleProfile diff --git a/MediaBrowser.WebDashboard/Api/PackageCreator.cs b/MediaBrowser.WebDashboard/Api/PackageCreator.cs index 0a995a845..8982b5739 100644 --- a/MediaBrowser.WebDashboard/Api/PackageCreator.cs +++ b/MediaBrowser.WebDashboard/Api/PackageCreator.cs @@ -364,7 +364,7 @@ namespace MediaBrowser.WebDashboard.Api "backdrops.js", "sync.js", "syncjob.js", - "syncservices.js", + "appservices.js", "playlistmanager.js", "mediaplayer.js", diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 03a704a46..2725d63ad 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -151,7 +151,7 @@ PreserveNewest - + PreserveNewest @@ -175,7 +175,7 @@ PreserveNewest - + PreserveNewest diff --git a/Nuget/MediaBrowser.Common.Internal.nuspec b/Nuget/MediaBrowser.Common.Internal.nuspec index dd28aae3a..40da3899c 100644 --- a/Nuget/MediaBrowser.Common.Internal.nuspec +++ b/Nuget/MediaBrowser.Common.Internal.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common.Internal - 3.0.608 + 3.0.609 MediaBrowser.Common.Internal Luke ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption. Copyright © Emby 2013 - + diff --git a/Nuget/MediaBrowser.Common.nuspec b/Nuget/MediaBrowser.Common.nuspec index 0124a609d..7aa9d3055 100644 --- a/Nuget/MediaBrowser.Common.nuspec +++ b/Nuget/MediaBrowser.Common.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Common - 3.0.608 + 3.0.609 MediaBrowser.Common Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Model.Signed.nuspec b/Nuget/MediaBrowser.Model.Signed.nuspec index 3297b6606..8d65ec431 100644 --- a/Nuget/MediaBrowser.Model.Signed.nuspec +++ b/Nuget/MediaBrowser.Model.Signed.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Model.Signed - 3.0.608 + 3.0.609 MediaBrowser.Model - Signed Edition Emby Team ebr,Luke,scottisafool diff --git a/Nuget/MediaBrowser.Server.Core.nuspec b/Nuget/MediaBrowser.Server.Core.nuspec index 0a749c98b..9a5264da9 100644 --- a/Nuget/MediaBrowser.Server.Core.nuspec +++ b/Nuget/MediaBrowser.Server.Core.nuspec @@ -2,7 +2,7 @@ MediaBrowser.Server.Core - 3.0.608 + 3.0.609 Media Browser.Server.Core Emby Team ebr,Luke,scottisafool @@ -12,7 +12,7 @@ Contains core components required to build plugins for Emby Server. Copyright © Emby 2013 - +