Browse Source

add setting to control transcodng throttle

Luke Pulverenti 10 năm trước cách đây
mục cha
commit
5f044cfd68
28 tập tin đã thay đổi với 466 bổ sung232 xóa
  1. 7 4
      MediaBrowser.Api/Playback/BaseStreamingService.cs
  2. 36 0
      MediaBrowser.Api/Playback/StreamState.cs
  3. 18 8
      MediaBrowser.Api/Playback/TranscodingThrottler.cs
  4. 6 2
      MediaBrowser.Dlna/Didl/DidlBuilder.cs
  5. 3 1
      MediaBrowser.Dlna/PlayTo/PlayToController.cs
  6. 1 1
      MediaBrowser.MediaEncoding/Encoder/AudioEncoder.cs
  7. 17 64
      MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs
  8. 45 9
      MediaBrowser.MediaEncoding/Encoder/EncodingJob.cs
  9. 69 112
      MediaBrowser.MediaEncoding/Encoder/EncodingJobFactory.cs
  10. 0 4
      MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs
  11. 1 1
      MediaBrowser.MediaEncoding/Encoder/VideoEncoder.cs
  12. 4 0
      MediaBrowser.Model/Configuration/EncodingOptions.cs
  13. 11 2
      MediaBrowser.Model/Dlna/ConditionProcessor.cs
  14. 6 2
      MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
  15. 4 2
      MediaBrowser.Model/Dlna/DeviceProfile.cs
  16. 4 1
      MediaBrowser.Model/Dlna/ProfileConditionValue.cs
  17. 10 3
      MediaBrowser.Model/Dlna/StreamBuilder.cs
  18. 36 0
      MediaBrowser.Model/Dlna/StreamInfo.cs
  19. 32 2
      MediaBrowser.Model/Dto/MediaSourceInfo.cs
  20. 2 1
      MediaBrowser.Server.Implementations/Localization/JavaScript/javascript.json
  21. 3 1
      MediaBrowser.Server.Implementations/Localization/Server/server.json
  22. 142 3
      MediaBrowser.Server.Implementations/Sync/CloudSyncProfile.cs
  23. 1 1
      MediaBrowser.WebDashboard/Api/PackageCreator.cs
  24. 2 2
      MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj
  25. 2 2
      Nuget/MediaBrowser.Common.Internal.nuspec
  26. 1 1
      Nuget/MediaBrowser.Common.nuspec
  27. 1 1
      Nuget/MediaBrowser.Model.Signed.nuspec
  28. 2 2
      Nuget/MediaBrowser.Server.Core.nuspec

+ 7 - 4
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;
             }

+ 36 - 0
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;
+        }
+
         /// <summary>
         /// Predicts the audio sample rate that will be in the output stream
         /// </summary>

+ 18 - 8
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<EncodingOptions>("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;
 

+ 6 - 2
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('?'));
 

+ 3 - 1
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();
             }

+ 1 - 1
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)
         {
         }
 

+ 17 - 64
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<double> 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);
-
-                    state.LiveTvStreamId = streamInfo.Id;
-
-                    state.MediaPath = streamInfo.Path;
-                    state.InputProtocol = streamInfo.Protocol;
+                    OpenToken = state.MediaSource.OpenToken
 
-                    await Task.Delay(1500, cancellationToken).ConfigureAwait(false);
+                }, false, cancellationToken).ConfigureAwait(false);
 
-                    AttachMediaStreamInfo(state, streamInfo, state.Options);
-                    checkCodecs = true;
-                }
+                AttachMediaStreamInfo(state, liveStreamResponse.MediaSource, state.Options);
 
-                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);
         }
 
         /// <summary>

+ 45 - 9
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<MediaStream> 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<string, string>(StringComparer.OrdinalIgnoreCase);
@@ -89,7 +90,6 @@ namespace MediaBrowser.MediaEncoding.Encoder
             SupportedAudioCodecs = new List<string>();
             PlayableStreamFileNames = new List<string>();
             RemoteHttpHeaders = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
-            AllMediaStreams = new List<MediaStream>();
             TaskCompletionSource = new TaskCompletionSource<bool>();
         }
 
@@ -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;

+ 69 - 112
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<MediaStream> mediaStreams = null;
-
             state.ItemType = item.GetType().Name;
 
-            if (item is ILiveTvRecording)
-            {
-                var recording = await _liveTvManager.GetInternalRecording(request.ItemId, cancellationToken).ConfigureAwait(false);
-
-                state.VideoType = VideoType.VideoFile;
-                state.IsInputVideo = string.Equals(recording.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
-
-                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);
-
-                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<MediaStream>();
-
-                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);
+            state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase);
 
-                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;
-                    }
+            var mediaSources = await _mediaSourceManager.GetPlayackMediaSources(request.ItemId, false, cancellationToken).ConfigureAwait(false);
 
-                    state.IsoType = mediaSource.IsoType;
+            var mediaSource = string.IsNullOrEmpty(request.MediaSourceId)
+               ? mediaSources.First()
+               : mediaSources.First(i => string.Equals(i.Id, request.MediaSourceId));
 
-                    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)
+            TryStreamCopy(state, request);
+
+            return state;
+        }
+
+        internal static void TryStreamCopy(EncodingJob state,
+            EncodingJobOptions videoRequest)
+        {
+            if (state.IsVideoRequest)
             {
-                if (state.VideoStream != null && CanStreamCopyVideo(request, state.VideoStream))
+                if (state.VideoStream != null && CanStreamCopyVideo(videoRequest, state.VideoStream))
                 {
                     state.OutputVideoCodec = "copy";
                 }
 
-                if (state.AudioStream != null && CanStreamCopyAudio(request, state.AudioStream, state.SupportedAudioCodecs))
+                if (state.AudioStream != null && CanStreamCopyAudio(videoRequest, state.AudioStream, state.SupportedAudioCodecs))
                 {
                     state.OutputAudioCodec = "copy";
                 }
             }
-
-            return state;
         }
 
         internal static void AttachMediaStreamInfo(EncodingJob state,
-            List<MediaStream> mediaStreams,
+            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;
         }
 
         /// <summary>
@@ -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)
             {

+ 0 - 4
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())

+ 1 - 1
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)
         {
         }
 

+ 4 - 0
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;
         }
     }
 }

+ 11 - 2
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);
             }

+ 6 - 2
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<string> orgPnValues = new List<string>();
 

+ 4 - 2
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;

+ 4 - 1
MediaBrowser.Model/Dlna/ProfileConditionValue.cs

@@ -17,6 +17,9 @@
         VideoTimestamp = 12,
         IsAnamorphic = 13,
         RefFrames = 14,
-        IsCabac = 15
+        IsCabac = 15,
+        NumAudioStreams = 16,
+        NumVideoStreams = 17,
+        IsSecondaryAudio
     }
 }

+ 10 - 3
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

+ 36 - 0
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<MediaStream> GetSelectableAudioStreams()
         {
             return GetSelectableStreams(MediaStreamType.Audio);

+ 32 - 2
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<string, string> RequiredHttpHeaders { get; set; }        
-        
+        public Dictionary<string, string> 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;
+        }
     }
 }

+ 2 - 1
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.",

+ 3 - 1
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."
 }

+ 142 - 3
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<CodecProfile>
             {
                 new CodecProfile
                 {
                     Type = CodecType.Video,
+                    Codec = "h264",
                     Conditions = new []
                     {
                         new ProfileCondition
@@ -65,23 +93,134 @@ 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 = 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
                         },
                         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

+ 1 - 1
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",

+ 2 - 2
MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj

@@ -151,7 +151,7 @@
     <Content Include="dashboard-ui\scripts\syncjob.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\scripts\syncservices.js">
+    <Content Include="dashboard-ui\scripts\appservices.js">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\scripts\syncsettings.js">
@@ -175,7 +175,7 @@
     <Content Include="dashboard-ui\syncjob.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
-    <Content Include="dashboard-ui\syncservices.html">
+    <Content Include="dashboard-ui\appservices.html">
       <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
     </Content>
     <Content Include="dashboard-ui\syncsettings.html">

+ 2 - 2
Nuget/MediaBrowser.Common.Internal.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common.Internal</id>
-        <version>3.0.608</version>
+        <version>3.0.609</version>
         <title>MediaBrowser.Common.Internal</title>
         <authors>Luke</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains common components shared by Emby Theater and Emby Server. Not intended for plugin developer consumption.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.608" />
+            <dependency id="MediaBrowser.Common" version="3.0.609" />
             <dependency id="NLog" version="3.2.0.0" />
             <dependency id="SimpleInjector" version="2.7.0" />
         </dependencies>

+ 1 - 1
Nuget/MediaBrowser.Common.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Common</id>
-        <version>3.0.608</version>
+        <version>3.0.609</version>
         <title>MediaBrowser.Common</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 1 - 1
Nuget/MediaBrowser.Model.Signed.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2011/08/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Model.Signed</id>
-        <version>3.0.608</version>
+        <version>3.0.609</version>
         <title>MediaBrowser.Model - Signed Edition</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>

+ 2 - 2
Nuget/MediaBrowser.Server.Core.nuspec

@@ -2,7 +2,7 @@
 <package xmlns="http://schemas.microsoft.com/packaging/2010/07/nuspec.xsd">
     <metadata>
         <id>MediaBrowser.Server.Core</id>
-        <version>3.0.608</version>
+        <version>3.0.609</version>
         <title>Media Browser.Server.Core</title>
         <authors>Emby Team</authors>
         <owners>ebr,Luke,scottisafool</owners>
@@ -12,7 +12,7 @@
         <description>Contains core components required to build plugins for Emby Server.</description>
         <copyright>Copyright © Emby 2013</copyright>
         <dependencies>
-            <dependency id="MediaBrowser.Common" version="3.0.608" />
+            <dependency id="MediaBrowser.Common" version="3.0.609" />
         </dependencies>
     </metadata>
     <files>