2
0
Эх сурвалжийг харах

add setting to control transcodng throttle

Luke Pulverenti 10 жил өмнө
parent
commit
5f044cfd68
28 өөрчлөгдсөн 466 нэмэгдсэн , 232 устгасан
  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>