Explorar el Código

update dynamic images

Luke Pulverenti hace 10 años
padre
commit
e8161cf8ac

+ 2 - 1
MediaBrowser.Api/MediaBrowser.Api.csproj

@@ -79,7 +79,8 @@
     <Compile Include="FilterService.cs" />
     <Compile Include="IHasDtoOptions.cs" />
     <Compile Include="Library\ChapterService.cs" />
-    <Compile Include="Playback\Hls\MpegDashService.cs" />
+    <Compile Include="Playback\Dash\ManifestBuilder.cs" />
+    <Compile Include="Playback\Dash\MpegDashService.cs" />
     <Compile Include="Playback\MediaInfoService.cs" />
     <Compile Include="Playback\TranscodingThrottler.cs" />
     <Compile Include="PlaylistService.cs" />

+ 220 - 0
MediaBrowser.Api/Playback/Dash/ManifestBuilder.cs

@@ -0,0 +1,220 @@
+using System;
+using System.Globalization;
+using System.Security;
+using System.Text;
+
+namespace MediaBrowser.Api.Playback.Dash
+{
+    public class ManifestBuilder
+    {
+        protected readonly CultureInfo UsCulture = new CultureInfo("en-US");
+
+        public string GetManifestText(StreamState state, string playlistUrl)
+        {
+            var builder = new StringBuilder();
+
+            var time = TimeSpan.FromTicks(state.RunTimeTicks.Value);
+
+            var duration = "PT" + time.Hours.ToString("00", UsCulture) + "H" + time.Minutes.ToString("00", UsCulture) + "M" + time.Seconds.ToString("00", UsCulture) + ".00S";
+
+            builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
+
+            builder.AppendFormat(
+                "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:mpeg:dash:schema:mpd:2011\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\" type=\"static\" mediaPresentationDuration=\"{0}\" minBufferTime=\"PT5.0S\">",
+                duration);
+
+            builder.Append("<ProgramInformation>");
+            builder.Append("</ProgramInformation>");
+
+            builder.Append("<Period start=\"PT0S\">");
+            builder.Append(GetVideoAdaptationSet(state, playlistUrl));
+            builder.Append(GetAudioAdaptationSet(state, playlistUrl));
+            builder.Append("</Period>");
+
+            builder.Append("</MPD>");
+
+            return builder.ToString();
+        }
+
+        private string GetVideoAdaptationSet(StreamState state, string playlistUrl)
+        {
+            var builder = new StringBuilder();
+
+            builder.Append("<AdaptationSet id=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">");
+            builder.Append(GetVideoRepresentationOpenElement(state));
+
+            AppendSegmentList(state, builder, "video", playlistUrl);
+
+            builder.Append("</Representation>");
+            builder.Append("</AdaptationSet>");
+
+            return builder.ToString();
+        }
+
+        private string GetAudioAdaptationSet(StreamState state, string playlistUrl)
+        {
+            var builder = new StringBuilder();
+
+            builder.Append("<AdaptationSet id=\"audio\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">");
+            builder.Append(GetAudioRepresentationOpenElement(state));
+
+            builder.Append("<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"6\" />");
+
+            AppendSegmentList(state, builder, "audio", playlistUrl);
+
+            builder.Append("</Representation>");
+            builder.Append("</AdaptationSet>");
+
+            return builder.ToString();
+        }
+
+        private string GetVideoRepresentationOpenElement(StreamState state)
+        {
+            var codecs = GetVideoCodecDescriptor(state);
+
+            var mime = "video/mp4";
+
+            var xml = "<Representation id=\"0\" mimeType=\"" + mime + "\" codecs=\"" + codecs + "\"";
+
+            if (state.OutputWidth.HasValue)
+            {
+                xml += " width=\"" + state.OutputWidth.Value.ToString(UsCulture) + "\"";
+            }
+            if (state.OutputHeight.HasValue)
+            {
+                xml += " height=\"" + state.OutputHeight.Value.ToString(UsCulture) + "\"";
+            }
+            if (state.OutputVideoBitrate.HasValue)
+            {
+                xml += " bandwidth=\"" + state.OutputVideoBitrate.Value.ToString(UsCulture) + "\"";
+            }
+
+            xml += ">";
+
+            return xml;
+        }
+
+        private string GetAudioRepresentationOpenElement(StreamState state)
+        {
+            var codecs = GetAudioCodecDescriptor(state);
+
+            var mime = "audio/mp4";
+
+            var xml = "<Representation id=\"1\" mimeType=\"" + mime + "\" codecs=\"" + codecs + "\"";
+
+            if (state.OutputAudioSampleRate.HasValue)
+            {
+                xml += " audioSamplingRate=\"" + state.OutputAudioSampleRate.Value.ToString(UsCulture) + "\"";
+            }
+            if (state.OutputAudioBitrate.HasValue)
+            {
+                xml += " bandwidth=\"" + state.OutputAudioBitrate.Value.ToString(UsCulture) + "\"";
+            }
+
+            xml += ">";
+
+            return xml;
+        }
+
+        private string GetVideoCodecDescriptor(StreamState state)
+        {
+            // https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
+            // http://www.chipwreck.de/blog/2010/02/25/html-5-video-tag-and-attributes/
+
+            var level = state.TargetVideoLevel ?? 0;
+            var profile = state.TargetVideoProfile ?? string.Empty;
+
+            if (profile.IndexOf("high", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                if (level >= 4.1)
+                {
+                    return "avc1.640028";
+                }
+
+                if (level >= 4)
+                {
+                    return "avc1.640028";
+                }
+
+                return "avc1.64001f";
+            }
+
+            if (profile.IndexOf("main", StringComparison.OrdinalIgnoreCase) != -1)
+            {
+                if (level >= 4)
+                {
+                    return "avc1.4d0028";
+                }
+
+                if (level >= 3.1)
+                {
+                    return "avc1.4d001f";
+                }
+
+                return "avc1.4d001e";
+            }
+
+            if (level >= 3.1)
+            {
+                return "avc1.42001f";
+            }
+
+            return "avc1.42E01E";
+        }
+
+        private string GetAudioCodecDescriptor(StreamState state)
+        {
+            // https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
+
+            if (string.Equals(state.OutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
+            {
+                return "mp4a.40.34";
+            }
+
+            // AAC 5ch
+            if (state.OutputAudioChannels.HasValue && state.OutputAudioChannels.Value >= 5)
+            {
+                return "mp4a.40.5";
+            }
+
+            // AAC 2ch
+            return "mp4a.40.2";
+        }
+
+        private void AppendSegmentList(StreamState state, StringBuilder builder, string type, string playlistUrl)
+        {
+            var extension = ".m4s";
+
+            var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
+
+            var queryStringIndex = playlistUrl.IndexOf('?');
+            var queryString = queryStringIndex == -1 ? string.Empty : playlistUrl.Substring(queryStringIndex);
+
+            var index = 0;
+            var duration = 1000000 * state.SegmentLength;
+            builder.AppendFormat("<SegmentList timescale=\"1000000\" duration=\"{0}\" startNumber=\"1\">", duration.ToString(CultureInfo.InvariantCulture));
+
+            while (seconds > 0)
+            {
+                var segmentUrl = string.Format("dash/{3}/{0}{1}{2}",
+                    index.ToString(UsCulture),
+                    extension,
+                    SecurityElement.Escape(queryString),
+                    type);
+
+                if (index == 0)
+                {
+                    builder.AppendFormat("<Initialization sourceURL=\"{0}\"/>", segmentUrl);
+                }
+                else
+                {
+                    builder.AppendFormat("<SegmentURL media=\"{0}\"/>", segmentUrl);
+                }
+
+                seconds -= state.SegmentLength;
+                index++;
+            }
+            builder.Append("</SegmentList>");
+        }
+    }
+}

+ 19 - 241
MediaBrowser.Api/Playback/Hls/MpegDashService.cs → MediaBrowser.Api/Playback/Dash/MpegDashService.cs

@@ -1,4 +1,5 @@
-using MediaBrowser.Common.IO;
+using MediaBrowser.Api.Playback.Hls;
+using MediaBrowser.Common.IO;
 using MediaBrowser.Common.Net;
 using MediaBrowser.Controller.Configuration;
 using MediaBrowser.Controller.Devices;
@@ -15,13 +16,11 @@ using System.Collections.Generic;
 using System.Globalization;
 using System.IO;
 using System.Linq;
-using System.Security;
-using System.Text;
 using System.Threading;
 using System.Threading.Tasks;
 using MimeTypes = MediaBrowser.Model.Net.MimeTypes;
 
-namespace MediaBrowser.Api.Playback.Hls
+namespace MediaBrowser.Api.Playback.Dash
 {
     /// <summary>
     /// Options is needed for chromecast. Threw Head in there since it's related
@@ -88,16 +87,6 @@ namespace MediaBrowser.Api.Playback.Hls
 
         private async Task<object> GetAsync(GetMasterManifest request, string method)
         {
-            if (string.Equals(request.AudioCodec, "copy", StringComparison.OrdinalIgnoreCase))
-            {
-                throw new ArgumentException("Audio codec copy is not allowed here.");
-            }
-
-            if (string.Equals(request.VideoCodec, "copy", StringComparison.OrdinalIgnoreCase))
-            {
-                throw new ArgumentException("Video codec copy is not allowed here.");
-            }
-
             if (string.IsNullOrEmpty(request.MediaSourceId))
             {
                 throw new ArgumentException("MediaSourceId is required");
@@ -109,225 +98,17 @@ namespace MediaBrowser.Api.Playback.Hls
 
             if (string.Equals(method, "GET", StringComparison.OrdinalIgnoreCase))
             {
-                playlistText = GetManifestText(state);
+                playlistText = new ManifestBuilder().GetManifestText(state, Request.RawUrl);
             }
 
             return ResultFactory.GetResult(playlistText, MimeTypes.GetMimeType("playlist.mpd"), new Dictionary<string, string>());
         }
 
-        private string GetManifestText(StreamState state)
-        {
-            var builder = new StringBuilder();
-
-            var time = TimeSpan.FromTicks(state.RunTimeTicks.Value);
-
-            var duration = "PT" + time.Hours.ToString("00", UsCulture) + "H" + time.Minutes.ToString("00", UsCulture) + "M" + time.Seconds.ToString("00", UsCulture) + ".00S";
-
-            builder.Append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
-
-            builder.AppendFormat(
-                "<MPD xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns=\"urn:mpeg:dash:schema:mpd:2011\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xsi:schemaLocation=\"urn:mpeg:DASH:schema:MPD:2011 http://standards.iso.org/ittf/PubliclyAvailableStandards/MPEG-DASH_schema_files/DASH-MPD.xsd\" profiles=\"urn:mpeg:dash:profile:isoff-live:2011\" type=\"static\" mediaPresentationDuration=\"{0}\" minBufferTime=\"PT5.0S\">",
-                duration);
-
-            builder.Append("<ProgramInformation>");
-            builder.Append("</ProgramInformation>");
-
-            builder.Append("<Period start=\"PT0S\">");
-            builder.Append(GetVideoAdaptationSet(state));
-            builder.Append(GetAudioAdaptationSet(state));
-            builder.Append("</Period>");
-
-            builder.Append("</MPD>");
-
-            return builder.ToString();
-        }
-
-        private string GetVideoAdaptationSet(StreamState state)
-        {
-            var builder = new StringBuilder();
-
-            builder.Append("<AdaptationSet id=\"video\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">");
-            builder.Append(GetVideoRepresentationOpenElement(state));
-
-            AppendSegmentList(state, builder, "video");
-
-            builder.Append("</Representation>");
-            builder.Append("</AdaptationSet>");
-
-            return builder.ToString();
-        }
-
-        private string GetAudioAdaptationSet(StreamState state)
-        {
-            var builder = new StringBuilder();
-
-            builder.Append("<AdaptationSet id=\"audio\" segmentAlignment=\"true\" bitstreamSwitching=\"true\">");
-            builder.Append(GetAudioRepresentationOpenElement(state));
-
-            builder.Append("<AudioChannelConfiguration schemeIdUri=\"urn:mpeg:dash:23003:3:audio_channel_configuration:2011\" value=\"6\" />");
-
-            AppendSegmentList(state, builder, "audio");
-
-            builder.Append("</Representation>");
-            builder.Append("</AdaptationSet>");
-
-            return builder.ToString();
-        }
-
-        private string GetVideoRepresentationOpenElement(StreamState state)
-        {
-            var codecs = GetVideoCodecDescriptor(state);
-
-            var mime = "video/mp4";
-
-            var xml = "<Representation id=\"0\" mimeType=\"" + mime + "\" codecs=\"" + codecs + "\"";
-
-            if (state.OutputWidth.HasValue)
-            {
-                xml += " width=\"" + state.OutputWidth.Value.ToString(UsCulture) + "\"";
-            }
-            if (state.OutputHeight.HasValue)
-            {
-                xml += " height=\"" + state.OutputHeight.Value.ToString(UsCulture) + "\"";
-            }
-            if (state.OutputVideoBitrate.HasValue)
-            {
-                xml += " bandwidth=\"" + state.OutputVideoBitrate.Value.ToString(UsCulture) + "\"";
-            }
-
-            xml += ">";
-
-            return xml;
-        }
-
-        private string GetAudioRepresentationOpenElement(StreamState state)
-        {
-            var codecs = GetAudioCodecDescriptor(state);
-
-            var mime = "audio/mp4";
-
-            var xml = "<Representation id=\"1\" mimeType=\"" + mime + "\" codecs=\"" + codecs + "\"";
-
-            if (state.OutputAudioSampleRate.HasValue)
-            {
-                xml += " audioSamplingRate=\"" + state.OutputAudioSampleRate.Value.ToString(UsCulture) + "\"";
-            }
-            if (state.OutputAudioBitrate.HasValue)
-            {
-                xml += " bandwidth=\"" + state.OutputAudioBitrate.Value.ToString(UsCulture) + "\"";
-            }
-
-            xml += ">";
-
-            return xml;
-        }
-
-        private string GetVideoCodecDescriptor(StreamState state)
-        {
-            // https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
-            // http://www.chipwreck.de/blog/2010/02/25/html-5-video-tag-and-attributes/
-
-            var level = state.TargetVideoLevel ?? 0;
-            var profile = state.TargetVideoProfile ?? string.Empty;
-
-            if (profile.IndexOf("high", StringComparison.OrdinalIgnoreCase) != -1)
-            {
-                if (level >= 4.1)
-                {
-                    return "avc1.640028";
-                }
-
-                if (level >= 4)
-                {
-                    return "avc1.640028";
-                }
-
-                return "avc1.64001f";
-            }
-
-            if (profile.IndexOf("main", StringComparison.OrdinalIgnoreCase) != -1)
-            {
-                if (level >= 4)
-                {
-                    return "avc1.4d0028";
-                }
-
-                if (level >= 3.1)
-                {
-                    return "avc1.4d001f";
-                }
-
-                return "avc1.4d001e";
-            }
-
-            if (level >= 3.1)
-            {
-                return "avc1.42001f";
-            }
-
-            return "avc1.42E01E";
-        }
-
-        private string GetAudioCodecDescriptor(StreamState state)
-        {
-            // https://developer.apple.com/library/ios/documentation/networkinginternet/conceptual/streamingmediaguide/FrequentlyAskedQuestions/FrequentlyAskedQuestions.html
-
-            if (string.Equals(state.OutputAudioCodec, "mp3", StringComparison.OrdinalIgnoreCase))
-            {
-                return "mp4a.40.34";
-            }
-
-            // AAC 5ch
-            if (state.OutputAudioChannels.HasValue && state.OutputAudioChannels.Value >= 5)
-            {
-                return "mp4a.40.5";
-            }
-
-            // AAC 2ch
-            return "mp4a.40.2";
-        }
-
         public object Get(GetDashSegment request)
         {
             return GetDynamicSegment(request, request.SegmentId, request.SegmentType).Result;
         }
 
-        private void AppendSegmentList(StreamState state, StringBuilder builder, string type)
-        {
-            var extension = GetSegmentFileExtension(state);
-
-            var seconds = TimeSpan.FromTicks(state.RunTimeTicks ?? 0).TotalSeconds;
-
-            var queryStringIndex = Request.RawUrl.IndexOf('?');
-            var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex);
-
-            var index = 0;
-            var duration = 1000000 * state.SegmentLength;
-            builder.AppendFormat("<SegmentList timescale=\"1000000\" duration=\"{0}\" startNumber=\"1\">", duration.ToString(CultureInfo.InvariantCulture));
-
-            while (seconds > 0)
-            {
-                var segmentUrl = string.Format("dash/{3}/{0}{1}{2}",
-                    index.ToString(UsCulture),
-                    extension,
-                    SecurityElement.Escape(queryString),
-                    type);
-
-                if (index == 0)
-                {
-                    builder.AppendFormat("<Initialization sourceURL=\"{0}\"/>", segmentUrl);
-                }
-                else
-                {
-                    builder.AppendFormat("<SegmentURL media=\"{0}\"/>", segmentUrl);
-                }
-
-                seconds -= state.SegmentLength;
-                index++;
-            }
-            builder.Append("</SegmentList>");
-        }
-
         private async Task<object> GetDynamicSegment(VideoStreamRequest request, string segmentId, string segmentType)
         {
             if ((request.StartTimeTicks ?? 0) > 0)
@@ -616,24 +397,6 @@ namespace MediaBrowser.Api.Playback.Hls
             }
         }
 
-        protected override int GetStartNumber(StreamState state)
-        {
-            return GetStartNumber(state.VideoRequest);
-        }
-
-        private int GetStartNumber(VideoStreamRequest request)
-        {
-            var segmentId = "0";
-
-            var segmentRequest = request as GetDynamicHlsVideoSegment;
-            if (segmentRequest != null)
-            {
-                segmentId = segmentRequest.SegmentId;
-            }
-
-            return int.Parse(segmentId, NumberStyles.Integer, UsCulture);
-        }
-
         private string GetSegmentPath(string playlist, string segmentType, string segmentExtension, int index)
         {
             var folder = Path.GetDirectoryName(playlist);
@@ -751,6 +514,21 @@ namespace MediaBrowser.Api.Playback.Hls
             return args;
         }
 
+        //private string GetCurrentTranscodingIndex(string outputPath)
+        //{
+
+        //}
+
+        //private string GetNextTranscodingIndex(string outputPath)
+        //{
+            
+        //}
+
+        //private string GetActualPlaylistPath(string outputPath, string transcodingIndex)
+        //{
+            
+        //}
+
         /// <summary>
         /// Gets the segment file extension.
         /// </summary>

+ 5 - 0
MediaBrowser.Controller/Entities/UserViewBuilder.cs

@@ -112,6 +112,11 @@ namespace MediaBrowser.Controller.Entities
                         return GetResult(result, queryParent, query);
                     }
 
+                case CollectionType.Books:
+                case CollectionType.Photos:
+                case CollectionType.HomeVideos:
+                    return GetResult(queryParent.GetChildren(user, true), queryParent, query);
+
                 case CollectionType.Folders:
                     return GetResult(user.RootFolder.GetChildren(user, true), queryParent, query);
 

+ 1 - 1
MediaBrowser.Server.Implementations/Library/LibraryManager.cs

@@ -1673,7 +1673,7 @@ namespace MediaBrowser.Server.Implementations.Library
                 throw new ArgumentNullException("viewType");
             }
 
-            var id = GetNewItemId("31_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty), typeof(UserView));
+            var id = GetNewItemId("35_namedview_" + name + user.Id.ToString("N") + (parentId ?? string.Empty), typeof(UserView));
 
             var path = Path.Combine(ConfigurationManager.ApplicationPaths.InternalMetadataPath, "views", id.ToString("N"));
 

+ 5 - 5
MediaBrowser.Server.Implementations/UserViews/DynamicImageProvider.cs

@@ -237,14 +237,14 @@ namespace MediaBrowser.Server.Implementations.UserViews
 
         protected override Task<Stream> CreateImageAsync(IHasImages item, List<BaseItem> itemsWithImages, ImageType imageType, int imageIndex)
         {
-            if (itemsWithImages.Count == 0)
-            {
-                return null;
-            }
-
             var view = (UserView)item;
             if (imageType == ImageType.Primary && IsUsingCollectionStrip(view))
             {
+                if (itemsWithImages.Count == 0 && !string.Equals(view.ViewType, CollectionType.LiveTv, StringComparison.OrdinalIgnoreCase))
+                {
+                    return null;
+                }
+
                 var stream = new StripCollageBuilder(ApplicationPaths).BuildThumbCollage(GetStripCollageImagePaths(itemsWithImages, view.ViewType), item.Name, 960, 540);
                 return Task.FromResult(stream);
             }