diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs
index ab20ab730a..7e352e4de1 100644
--- a/MediaBrowser.Api/Playback/BaseStreamingService.cs
+++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs
@@ -542,7 +542,7 @@ namespace MediaBrowser.Api.Playback
                 var maxWidthParam = request.MaxWidth.Value.ToString(UsCulture);
 
                 return isH264Output ?
-                    string.Format("{3} -vf \"{0}scale=min(iw\\,{1}):trunc(ow/a/2)*2{2}\"", yadifParam, maxWidthParam, assSubtitleParam, copyTsParam) :
+                    string.Format("{3} -vf \"{0}scale=min(iw\\,{1}):trunc(ow/dar/2)*2{2}\"", yadifParam, maxWidthParam, assSubtitleParam, copyTsParam) :
                     string.Format("{3} -vf \"{0}scale=min(iw\\,{1}):-1{2}\"", yadifParam, maxWidthParam, assSubtitleParam, copyTsParam);
             }
 
diff --git a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
index 8366e6fb9f..083ec8c82e 100644
--- a/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
+++ b/MediaBrowser.Dlna/Ssdp/SsdpHandler.cs
@@ -147,7 +147,7 @@ namespace MediaBrowser.Dlna.Ssdp
 
                     SendDatagram(header, values, endpoint, null);
 
-                    _logger.Info("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString());
+                    _logger.Debug("{1} - Responded to a {0} request to {2}", d.Type, endpoint, d.Address.ToString());
                 }
             }
         }
diff --git a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
index 7e21e1ef2f..5a6be389d0 100644
--- a/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
+++ b/MediaBrowser.Model/Dlna/ContentFeatureBuilder.cs
@@ -1,8 +1,6 @@
-using System;
+using MediaBrowser.Model.MediaInfo;
 using System.Collections.Generic;
 using System.Linq;
-using MediaBrowser.Model.Entities;
-using MediaBrowser.Model.MediaInfo;
 
 namespace MediaBrowser.Model.Dlna
 {
diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
index dc3a04769b..662bbdf3e5 100644
--- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
+++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs
@@ -11,6 +11,7 @@ using MediaBrowser.Model.Dto;
 using MediaBrowser.Model.Entities;
 using MediaBrowser.Model.Logging;
 using MediaBrowser.Model.Querying;
+using MediaBrowser.Model.Serialization;
 using System;
 using System.Collections.Generic;
 using System.IO;
@@ -33,8 +34,9 @@ namespace MediaBrowser.Server.Implementations.Channels
         private readonly ILogger _logger;
         private readonly IServerConfigurationManager _config;
         private readonly IFileSystem _fileSystem;
+        private readonly IJsonSerializer _jsonSerializer;
 
-        public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager)
+        public ChannelManager(IUserManager userManager, IDtoService dtoService, ILibraryManager libraryManager, ILogger logger, IServerConfigurationManager config, IFileSystem fileSystem, IUserDataManager userDataManager, IJsonSerializer jsonSerializer)
         {
             _userManager = userManager;
             _dtoService = dtoService;
@@ -43,6 +45,7 @@ namespace MediaBrowser.Server.Implementations.Channels
             _config = config;
             _fileSystem = fileSystem;
             _userDataManager = userDataManager;
+            _jsonSerializer = jsonSerializer;
         }
 
         public void AddParts(IEnumerable<IChannel> channels, IEnumerable<IChannelFactory> factories)
@@ -227,19 +230,90 @@ namespace MediaBrowser.Server.Implementations.Channels
             return await GetReturnItems(items, user, query, cancellationToken).ConfigureAwait(false);
         }
 
+        private readonly SemaphoreSlim _resourcePool = new SemaphoreSlim(1, 1);
         private async Task<IEnumerable<ChannelItemInfo>> GetChannelItems(IChannel channel, User user, string categoryId, CancellationToken cancellationToken)
         {
-            // TODO: Put some caching in here
+            var cachePath = GetChannelDataCachePath(channel, user, categoryId);
 
-            var query = new InternalChannelItemQuery
+            try
             {
-                User = user,
-                CategoryId = categoryId
-            };
+                var channelItemResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+
+                if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(channelItemResult.CacheLength) > DateTime.UtcNow)
+                {
+                    return channelItemResult.Items;
+                }
+            }
+            catch (FileNotFoundException)
+            {
+
+            }
+            catch (DirectoryNotFoundException)
+            {
+
+            }
+
+            await _resourcePool.WaitAsync(cancellationToken).ConfigureAwait(false);
+
+            try
+            {
+                try
+                {
+                    var channelItemResult = _jsonSerializer.DeserializeFromFile<ChannelItemResult>(cachePath);
+
+                    if (_fileSystem.GetLastWriteTimeUtc(cachePath).Add(channelItemResult.CacheLength) > DateTime.UtcNow)
+                    {
+                        return channelItemResult.Items;
+                    }
+                }
+                catch (FileNotFoundException)
+                {
+
+                }
+                catch (DirectoryNotFoundException)
+                {
+
+                }
+
+                var query = new InternalChannelItemQuery
+                {
+                    User = user,
+                    CategoryId = categoryId
+                };
+
+                var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
+
+                CacheResponse(result, cachePath);
+
+                return result.Items;
+            }
+            finally
+            {
+                _resourcePool.Release();
+            }
+        }
+
+        private void CacheResponse(ChannelItemResult result, string path)
+        {
+            try
+            {
+                Directory.CreateDirectory(Path.GetDirectoryName(path));
+
+                _jsonSerializer.SerializeToFile(result, path);
+            }
+            catch (Exception ex)
+            {
+                _logger.ErrorException("Error writing to channel cache file: {0}", ex, path);
+            }
+        }
+
+        private string GetChannelDataCachePath(IChannel channel, User user, string categoryId)
+        {
+            var channelId = GetInternalChannelId(channel.Name).ToString("N");
 
-            var result = await channel.GetChannelItems(query, cancellationToken).ConfigureAwait(false);
+            var categoryKey = string.IsNullOrWhiteSpace(categoryId) ? "root" : categoryId.GetMD5().ToString("N");
 
-            return result.Items;
+            return Path.Combine(_config.ApplicationPaths.CachePath, channelId, categoryKey, user.Id.ToString("N") + ".json");
         }
 
         private async Task<QueryResult<BaseItemDto>> GetReturnItems(IEnumerable<ChannelItemInfo> items, User user, ChannelItemQuery query, CancellationToken cancellationToken)
diff --git a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs
index cc32217118..43e968fa55 100644
--- a/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs
+++ b/MediaBrowser.Server.Implementations/EntryPoints/Notifications/Notifier.cs
@@ -164,6 +164,12 @@ namespace MediaBrowser.Server.Implementations.EntryPoints.Notifications
 
             var item = e.MediaInfo;
 
+            if (item == null)
+            {
+                _logger.Warn("PlaybackStart reported with null media info.");
+                return;
+            }
+
             if (e.Item != null && e.Item.Parent == null)
             {
                 // Don't report theme song or local trailer playback
diff --git a/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs b/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs
index f02c907c66..86d88f7e04 100644
--- a/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs
+++ b/MediaBrowser.Server.Implementations/Library/Validators/BoxSetPostScanTask.cs
@@ -27,7 +27,7 @@ namespace MediaBrowser.Server.Implementations.Library.Validators
 
             foreach (var boxset in boxsets)
             {
-                foreach (var child in boxset.GetLinkedChildren().OfType<ISupportsBoxSetGrouping>())
+                foreach (var child in boxset.Children.Concat(boxset.GetLinkedChildren()).OfType<ISupportsBoxSetGrouping>())
                 {
                     var boxsetIdList = child.BoxSetIdList.ToList();
                     if (!boxsetIdList.Contains(boxset.Id))
diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs
index 555899f3ba..befcd701e3 100644
--- a/MediaBrowser.ServerApplication/ApplicationHost.cs
+++ b/MediaBrowser.ServerApplication/ApplicationHost.cs
@@ -507,7 +507,7 @@ namespace MediaBrowser.ServerApplication
                 MediaEncoder);
             RegisterSingleInstance(EncodingManager);
 
-            ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager);
+            ChannelManager = new ChannelManager(UserManager, DtoService, LibraryManager, Logger, ServerConfigurationManager, FileSystemManager, UserDataManager, JsonSerializer);
             RegisterSingleInstance(ChannelManager);
 
             var appThemeManager = new AppThemeManager(ApplicationPaths, FileSystemManager, JsonSerializer, Logger);