From 8eb4c034b44f9c414ec19e7f0ab40c2070a8cf64 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 15 Apr 2017 15:45:33 -0400 Subject: [PATCH] update audio sync params --- MediaBrowser.Api/MediaBrowser.Api.csproj | 1 + .../Playback/Hls/DynamicHlsService.cs | 5 + .../Playback/Hls/VideoHlsService.cs | 8 +- MediaBrowser.Api/SuggestionsService.cs | 104 ++++++++++++++++++ MediaBrowser.Api/TvShowsService.cs | 36 +++++- .../Entities/Audio/MusicAlbum.cs | 33 ++++++ .../MediaEncoding/EncodingHelper.cs | 48 ++++---- .../MediaEncoding/EncodingJobInfo.cs | 2 +- .../Encoder/MediaEncoder.cs | 26 ++--- 9 files changed, 207 insertions(+), 56 deletions(-) create mode 100644 MediaBrowser.Api/SuggestionsService.cs diff --git a/MediaBrowser.Api/MediaBrowser.Api.csproj b/MediaBrowser.Api/MediaBrowser.Api.csproj index c2c8d1102b..0785b904ae 100644 --- a/MediaBrowser.Api/MediaBrowser.Api.csproj +++ b/MediaBrowser.Api/MediaBrowser.Api.csproj @@ -134,6 +134,7 @@ + diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 43f25145e0..038d762456 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -826,6 +826,11 @@ namespace MediaBrowser.Api.Playback.Hls args += " -ab " + bitrate.Value.ToString(UsCulture); } + if (state.OutputAudioSampleRate.HasValue) + { + args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture); + } + args += " " + EncodingHelper.GetAudioFilterParam(state, ApiEntryPoint.Instance.GetEncodingOptions(), true); return args; diff --git a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs index 71b1dc1b15..a813d7a872 100644 --- a/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/VideoHlsService.cs @@ -6,10 +6,7 @@ using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.IO; using MediaBrowser.Model.Serialization; using System; -using MediaBrowser.Common.IO; -using MediaBrowser.Controller.IO; using MediaBrowser.Controller.Net; -using MediaBrowser.Model.IO; using MediaBrowser.Model.Dlna; using MediaBrowser.Model.Services; @@ -60,6 +57,11 @@ namespace MediaBrowser.Api.Playback.Hls args += " -ab " + bitrate.Value.ToString(UsCulture); } + if (state.OutputAudioSampleRate.HasValue) + { + args += " -ar " + state.OutputAudioSampleRate.Value.ToString(UsCulture); + } + args += " " + EncodingHelper.GetAudioFilterParam(state, ApiEntryPoint.Instance.GetEncodingOptions(), true); return args; diff --git a/MediaBrowser.Api/SuggestionsService.cs b/MediaBrowser.Api/SuggestionsService.cs new file mode 100644 index 0000000000..7196c0f392 --- /dev/null +++ b/MediaBrowser.Api/SuggestionsService.cs @@ -0,0 +1,104 @@ +using MediaBrowser.Controller.Dto; +using MediaBrowser.Controller.Entities; +using MediaBrowser.Controller.Net; +using MediaBrowser.Model.Dto; +using MediaBrowser.Model.Querying; +using MediaBrowser.Model.Services; +using System; +using System.Linq; +using System.Threading.Tasks; +using MediaBrowser.Controller.Library; + +namespace MediaBrowser.Api +{ + [Route("/Users/{UserId}/Suggestions", "GET", Summary = "Gets items based on a query.")] + public class GetSuggestedItems : IReturn> + { + public string MediaType { get; set; } + public string Type { get; set; } + public string UserId { get; set; } + public bool EnableTotalRecordCount { get; set; } + public int? StartIndex { get; set; } + public int? Limit { get; set; } + + public string[] GetMediaTypes() + { + return (MediaType ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + + public string[] GetIncludeItemTypes() + { + return (Type ?? string.Empty).Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries); + } + } + + public class SuggestionsService : BaseApiService + { + private readonly IDtoService _dtoService; + private readonly IAuthorizationContext _authContext; + private readonly IUserManager _userManager; + private readonly ILibraryManager _libraryManager; + + public SuggestionsService(IDtoService dtoService, IAuthorizationContext authContext, IUserManager userManager, ILibraryManager libraryManager) + { + _dtoService = dtoService; + _authContext = authContext; + _userManager = userManager; + _libraryManager = libraryManager; + } + + public async Task Get(GetSuggestedItems request) + { + var result = await GetResultItems(request).ConfigureAwait(false); + + return ToOptimizedResult(result); + } + + private async Task> GetResultItems(GetSuggestedItems request) + { + var user = !string.IsNullOrWhiteSpace(request.UserId) ? _userManager.GetUserById(request.UserId) : null; + + var dtoOptions = GetDtoOptions(_authContext, request); + var result = GetItems(request, user, dtoOptions); + + var dtoList = await _dtoService.GetBaseItemDtos(result.Items, dtoOptions, user).ConfigureAwait(false); + + if (dtoList == null) + { + throw new InvalidOperationException("GetBaseItemDtos returned null"); + } + + return new QueryResult + { + TotalRecordCount = result.TotalRecordCount, + Items = dtoList.ToArray() + }; + } + + private QueryResult GetItems(GetSuggestedItems request, User user, DtoOptions dtoOptions) + { + var query = new InternalItemsQuery(user) + { + SortBy = new string[] { ItemSortBy.Random }, + MediaTypes = request.GetMediaTypes(), + IncludeItemTypes = request.GetIncludeItemTypes(), + IsVirtualItem = false, + StartIndex = request.StartIndex, + Limit = request.Limit, + DtoOptions = dtoOptions + }; + + if (request.EnableTotalRecordCount) + { + return _libraryManager.GetItemsResult(query); + } + + var items = _libraryManager.GetItemList(query).ToArray(); + + return new QueryResult + { + Items = items + }; + } + } +} diff --git a/MediaBrowser.Api/TvShowsService.cs b/MediaBrowser.Api/TvShowsService.cs index 43fe74d5fb..d7f017e1dd 100644 --- a/MediaBrowser.Api/TvShowsService.cs +++ b/MediaBrowser.Api/TvShowsService.cs @@ -137,6 +137,7 @@ namespace MediaBrowser.Api } [Route("/Shows/{Id}/Episodes", "GET", Summary = "Gets episodes for a tv season")] + [Route("/Shows/Episodes", "GET", Summary = "Gets episodes for a tv season")] public class GetEpisodes : IReturn, IHasItemFields, IHasDtoOptions { /// @@ -200,9 +201,11 @@ namespace MediaBrowser.Api [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableUserData { get; set; } + public string SeriesName { get; set; } } [Route("/Shows/{Id}/Seasons", "GET", Summary = "Gets seasons for a tv series")] + [Route("/Shows/Seasons", "GET", Summary = "Gets seasons for a tv series")] public class GetSeasons : IReturn, IHasItemFields, IHasDtoOptions { /// @@ -246,6 +249,7 @@ namespace MediaBrowser.Api [ApiMember(Name = "EnableUserData", Description = "Optional, include user data", IsRequired = false, DataType = "boolean", ParameterType = "query", Verb = "GET")] public bool? EnableUserData { get; set; } + public string SeriesName { get; set; } } /// @@ -427,11 +431,11 @@ namespace MediaBrowser.Api { var user = _userManager.GetUserById(request.UserId); - var series = _libraryManager.GetItemById(request.Id) as Series; + var series = GetSeries(request.Id, request.SeriesName, user); if (series == null) { - throw new ResourceNotFoundException("No series exists with Id " + request.Id); + throw new ResourceNotFoundException("Series not found"); } var seasons = (await series.GetItems(new InternalItemsQuery(user) @@ -455,6 +459,26 @@ namespace MediaBrowser.Api }; } + private Series GetSeries(string seriesId, string seriesName, User user) + { + if (!string.IsNullOrWhiteSpace(seriesId)) + { + return _libraryManager.GetItemById(seriesId) as Series; + } + + if (!string.IsNullOrWhiteSpace(seriesName)) + { + return _libraryManager.GetItemList(new InternalItemsQuery(user) + { + Name = seriesName, + IncludeItemTypes = new string[] { typeof(Series).Name } + + }).OfType().FirstOrDefault(); + } + + return null; + } + public async Task Get(GetEpisodes request) { var user = _userManager.GetUserById(request.UserId); @@ -474,11 +498,11 @@ namespace MediaBrowser.Api } else if (request.Season.HasValue) { - var series = _libraryManager.GetItemById(request.Id) as Series; + var series = GetSeries(request.Id, request.SeriesName, user); if (series == null) { - throw new ResourceNotFoundException("No series exists with Id " + request.Id); + throw new ResourceNotFoundException("Series not found"); } var season = series.GetSeasons(user).FirstOrDefault(i => i.IndexNumber == request.Season.Value); @@ -494,11 +518,11 @@ namespace MediaBrowser.Api } else { - var series = _libraryManager.GetItemById(request.Id) as Series; + var series = GetSeries(request.Id, request.SeriesName, user); if (series == null) { - throw new ResourceNotFoundException("No series exists with Id " + request.Id); + throw new ResourceNotFoundException("Series not found"); } episodes = series.GetEpisodes(user); diff --git a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs index 6ad38033a4..f40ab3cdee 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicAlbum.cs @@ -9,6 +9,7 @@ using MediaBrowser.Model.Serialization; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Controller.Library; +using MediaBrowser.Model.Dto; namespace MediaBrowser.Controller.Entities.Audio { @@ -227,7 +228,39 @@ namespace MediaBrowser.Controller.Entities.Audio // Refresh current item await RefreshMetadata(parentRefreshOptions, cancellationToken).ConfigureAwait(false); + if (!refreshOptions.IsAutomated) + { + await RefreshArtists(refreshOptions, cancellationToken).ConfigureAwait(false); + } + progress.Report(100); } + + private async Task RefreshArtists(MetadataRefreshOptions refreshOptions, CancellationToken cancellationToken) + { + var artists = AllArtists.Select(i => + { + // This should not be necessary but we're seeing some cases of it + if (string.IsNullOrWhiteSpace(i)) + { + return null; + } + + var artist = LibraryManager.GetArtist(i); + + if (!artist.IsAccessedByName) + { + return null; + } + + return artist; + + }).Where(i => i != null).ToList(); + + foreach (var artist in artists) + { + await artist.RefreshMetadata(refreshOptions, cancellationToken).ConfigureAwait(false); + } + } } } diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 8412985bfa..dd1397a0d0 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -719,14 +719,15 @@ namespace MediaBrowser.Controller.MediaEncoding } } // nvenc doesn't decode with param -level set ?! - else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)){ + else if (string.Equals(videoEncoder, "h264_nvenc", StringComparison.OrdinalIgnoreCase)) + { //param += ""; } else if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase)) { param += " -level " + level; } - + } if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) @@ -1014,43 +1015,32 @@ namespace MediaBrowser.Controller.MediaEncoding public string GetAudioFilterParam(EncodingJobInfo state, EncodingOptions encodingOptions, bool isHls) { - var volParam = string.Empty; - var audioSampleRate = string.Empty; - var channels = state.OutputAudioChannels; + var filters = new List(); + // Boost volume to 200% when downsampling from 6ch to 2ch if (channels.HasValue && channels.Value <= 2) { if (state.AudioStream != null && state.AudioStream.Channels.HasValue && state.AudioStream.Channels.Value > 5 && !encodingOptions.DownMixAudioBoost.Equals(1)) { - volParam = ",volume=" + encodingOptions.DownMixAudioBoost.ToString(_usCulture); + filters.Add("volume=" + encodingOptions.DownMixAudioBoost.ToString(_usCulture)); } } - if (state.OutputAudioSampleRate.HasValue) - { - audioSampleRate = state.OutputAudioSampleRate.Value + ":"; - } - - var adelay = isHls ? "adelay=1," : string.Empty; - - var pts = string.Empty; - if (state.SubtitleStream != null && state.SubtitleStream.IsTextSubtitleStream && state.SubtitleDeliveryMethod == SubtitleDeliveryMethod.Encode && !state.CopyTimestamps) { var seconds = TimeSpan.FromTicks(state.StartTimeTicks ?? 0).TotalSeconds; - pts = string.Format(",asetpts=PTS-{0}/TB", Math.Round(seconds).ToString(_usCulture)); + filters.Add(string.Format("asetpts=PTS-{0}/TB", Math.Round(seconds).ToString(_usCulture))); } - return string.Format("-af \"{0}aresample={1}async={4}{2}{3}\"", + if (filters.Count > 0) + { + return "-af \"" + string.Join(",", filters.ToArray()) + "\""; + } - adelay, - audioSampleRate, - volParam, - pts, - state.OutputAudioSync); + return string.Empty; } /// @@ -1282,7 +1272,7 @@ namespace MediaBrowser.Controller.MediaEncoding } var videoSizeParam = string.Empty; - + if (state.VideoStream != null && state.VideoStream.Width.HasValue && state.VideoStream.Height.HasValue) { videoSizeParam = string.Format("scale={0}:{1}", state.VideoStream.Width.Value.ToString(_usCulture), state.VideoStream.Height.Value.ToString(_usCulture)); @@ -1569,7 +1559,7 @@ namespace MediaBrowser.Controller.MediaEncoding var outputVideoCodec = GetVideoEncoder(state, encodingOptions); // Important: If this is ever re-enabled, make sure not to use it with wtv because it breaks seeking - if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) && + if (!string.Equals(state.InputContainer, "wtv", StringComparison.OrdinalIgnoreCase) && state.TranscodingType != TranscodingJobType.Progressive && state.EnableBreakOnNonKeyFrames(outputVideoCodec)) { @@ -1657,7 +1647,6 @@ namespace MediaBrowser.Controller.MediaEncoding if (state.ReadInputAtNativeFramerate || mediaSource.Protocol == MediaProtocol.File && string.Equals(mediaSource.Container, "wtv", StringComparison.OrdinalIgnoreCase)) { - state.OutputAudioSync = "1000"; state.InputVideoSync = "-1"; state.InputAudioSync = "1"; } @@ -1715,7 +1704,7 @@ namespace MediaBrowser.Controller.MediaEncoding { if (state.SubtitleStream == null || state.SubtitleDeliveryMethod != SubtitleDeliveryMethod.Embed) { - return ; + return; } // This is tricky to remux in, after converting to dvdsub it's not positioned correctly @@ -1985,7 +1974,12 @@ namespace MediaBrowser.Controller.MediaEncoding { args += " -ab " + bitrate.Value.ToString(_usCulture); } - + + if (state.OutputAudioSampleRate.HasValue) + { + args += " -ar " + state.OutputAudioSampleRate.Value.ToString(_usCulture); + } + args += " " + GetAudioFilterParam(state, encodingOptions, false); return args; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs index 4e0f223b77..f3e6280aaf 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingJobInfo.cs @@ -55,7 +55,7 @@ namespace MediaBrowser.Controller.MediaEncoding return "-1"; } } - public string OutputAudioSync = "1"; + public string InputAudioSync { get; set; } public string InputVideoSync { get; set; } public TransportStreamTimestamp InputTimestamp { get; set; } diff --git a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs index af826f231b..816f14f821 100644 --- a/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/MediaEncoder.cs @@ -664,28 +664,16 @@ namespace MediaBrowser.MediaEncoding.Encoder private bool DetectInterlaced(MediaSourceInfo video, MediaStream videoStream) { - var formats = (video.Container ?? string.Empty).Split(',').ToList(); - var enableInterlacedDection = formats.Contains("vob", StringComparer.OrdinalIgnoreCase) || - formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) || - formats.Contains("ts", StringComparer.OrdinalIgnoreCase) || - formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) || - formats.Contains("wtv", StringComparer.OrdinalIgnoreCase); - // If it's mpeg based, assume true if ((videoStream.Codec ?? string.Empty).IndexOf("mpeg", StringComparison.OrdinalIgnoreCase) != -1) { - if (enableInterlacedDection) - { - return true; - } - } - else - { - // If the video codec is not some form of mpeg, then take a shortcut and limit this to containers that are likely to have interlaced content - if (!enableInterlacedDection) - { - return false; - } + var formats = (video.Container ?? string.Empty).Split(',').ToList(); + return formats.Contains("vob", StringComparer.OrdinalIgnoreCase) || + formats.Contains("m2ts", StringComparer.OrdinalIgnoreCase) || + formats.Contains("ts", StringComparer.OrdinalIgnoreCase) || + formats.Contains("mpegts", StringComparer.OrdinalIgnoreCase) || + formats.Contains("wtv", StringComparer.OrdinalIgnoreCase); + } return false;