diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index 1b82349734..380ece2f2d 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -1417,7 +1417,6 @@ namespace MediaBrowser.Api.Playback List mediaStreams = null; state.ItemType = item.GetType().Name; - state.ReadInputAtNativeFramerate = true; if (item is ILiveTvRecording) { @@ -1479,6 +1478,7 @@ namespace MediaBrowser.Api.Playback state.IsInputVideo = string.Equals(channel.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); mediaStreams = new List(); + state.ReadInputAtNativeFramerate = true; state.OutputAudioSync = "1000"; state.DeInterlace = true; state.InputVideoSync = "-1"; @@ -1489,13 +1489,13 @@ namespace MediaBrowser.Api.Playback } else if (item is IChannelMediaItem) { - var source = await GetChannelMediaInfo(request.Id, request.MediaSourceId, cancellationToken).ConfigureAwait(false); + var mediaSource = await GetChannelMediaInfo(request.Id, request.MediaSourceId, cancellationToken).ConfigureAwait(false); state.IsInputVideo = string.Equals(item.MediaType, MediaType.Video, StringComparison.OrdinalIgnoreCase); - state.InputProtocol = source.Protocol; - state.MediaPath = source.Path; + state.InputProtocol = mediaSource.Protocol; + state.MediaPath = mediaSource.Path; state.RunTimeTicks = item.RunTimeTicks; - state.RemoteHttpHeaders = source.RequiredHttpHeaders; - mediaStreams = source.MediaStreams; + state.RemoteHttpHeaders = mediaSource.RequiredHttpHeaders; + mediaStreams = mediaSource.MediaStreams; } else { @@ -1539,6 +1539,11 @@ namespace MediaBrowser.Api.Playback state.DeInterlace = true; } + if (state.InputProtocol == MediaProtocol.Rtmp) + { + state.ReadInputAtNativeFramerate = true; + } + var videoRequest = request as VideoStreamRequest; AttachMediaStreamInfo(state, mediaStreams, videoRequest, url); diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index e41a331be9..57c2244c7a 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -119,9 +119,8 @@ namespace MediaBrowser.Api.Playback.Hls } } - int audioBitrate; - int videoBitrate; - GetPlaylistBitrates(state, out audioBitrate, out videoBitrate); + var audioBitrate = state.OutputAudioBitrate ?? 0; + var videoBitrate = state.OutputVideoBitrate ?? 0; var appendBaselineStream = false; var baselineStreamBitrate = 64000; @@ -162,37 +161,6 @@ namespace MediaBrowser.Api.Playback.Hls return minimumSegmentCount; } - /// - /// Gets the playlist bitrates. - /// - /// The state. - /// The audio bitrate. - /// The video bitrate. - protected void GetPlaylistBitrates(StreamState state, out int audioBitrate, out int videoBitrate) - { - var audioBitrateParam = state.OutputAudioBitrate; - var videoBitrateParam = state.OutputVideoBitrate; - - if (!audioBitrateParam.HasValue) - { - if (state.AudioStream != null) - { - audioBitrateParam = state.AudioStream.BitRate; - } - } - - if (!videoBitrateParam.HasValue) - { - if (state.VideoStream != null) - { - videoBitrateParam = state.VideoStream.BitRate; - } - } - - audioBitrate = audioBitrateParam ?? 0; - videoBitrate = videoBitrateParam ?? 0; - } - private string GetMasterPlaylistFileText(string firstPlaylist, int bitrate, bool includeBaselineStream, int baselineStreamBitrate) { var builder = new StringBuilder(); diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 5913e81db8..b5b6849cef 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -304,45 +304,79 @@ namespace MediaBrowser.Api.Playback.Hls { var state = await GetState(request, CancellationToken.None).ConfigureAwait(false); - int audioBitrate; - int videoBitrate; - GetPlaylistBitrates(state, out audioBitrate, out videoBitrate); + var audioBitrate = state.OutputAudioBitrate ?? 0; + var videoBitrate = state.OutputVideoBitrate ?? 0; - var appendBaselineStream = false; - var baselineStreamBitrate = 64000; - - var hlsVideoRequest = state.VideoRequest as GetMasterHlsVideoStream; - if (hlsVideoRequest != null) - { - appendBaselineStream = hlsVideoRequest.AppendBaselineStream; - baselineStreamBitrate = hlsVideoRequest.BaselineStreamAudioBitRate ?? baselineStreamBitrate; - } - - var playlistText = GetMasterPlaylistFileText(videoBitrate + audioBitrate); + var playlistText = GetMasterPlaylistFileText(state, videoBitrate + audioBitrate); return ResultFactory.GetResult(playlistText, Common.Net.MimeTypes.GetMimeType("playlist.m3u8"), new Dictionary()); } - private string GetMasterPlaylistFileText(int bitrate) + private string GetMasterPlaylistFileText(StreamState state, int totalBitrate) { var builder = new StringBuilder(); builder.AppendLine("#EXTM3U"); - // Pad a little to satisfy the apple hls validator - var paddedBitrate = Convert.ToInt32(bitrate * 1.05); - var queryStringIndex = Request.RawUrl.IndexOf('?'); var queryString = queryStringIndex == -1 ? string.Empty : Request.RawUrl.Substring(queryStringIndex); // Main stream - builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + paddedBitrate.ToString(UsCulture)); var playlistUrl = "main.m3u8" + queryString; - builder.AppendLine(playlistUrl); + AppendPlaylist(builder, playlistUrl, totalBitrate); + + if (state.VideoRequest.VideoBitRate.HasValue) + { + var requestedVideoBitrate = state.VideoRequest.VideoBitRate.Value; + + // By default, vary by just 200k + var variation = GetBitrateVariation(totalBitrate); + + var newBitrate = totalBitrate - variation; + AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - variation).ToString(UsCulture)), newBitrate); + + newBitrate = totalBitrate - (2 * variation); + AppendPlaylist(builder, playlistUrl.Replace(requestedVideoBitrate.ToString(UsCulture), (requestedVideoBitrate - (2 * variation)).ToString(UsCulture)), newBitrate); + } return builder.ToString(); } + private void AppendPlaylist(StringBuilder builder, string url, int bitrate) + { + builder.AppendLine("#EXT-X-STREAM-INF:PROGRAM-ID=1,BANDWIDTH=" + bitrate.ToString(UsCulture)); + builder.AppendLine(url); + } + + private int GetBitrateVariation(int bitrate) + { + // By default, vary by just 200k + var variation = 200000; + + if (bitrate >= 10000000) + { + variation = 2000000; + } + else if (bitrate >= 5000000) + { + variation = 1500000; + } + else if (bitrate >= 3000000) + { + variation = 1000000; + } + else if (bitrate >= 2000000) + { + variation = 500000; + } + else if (bitrate >= 1000000) + { + variation = 300000; + } + + return variation; + } + public object Get(GetMainHlsVideoStream request) { var result = GetPlaylistAsync(request, "main").Result; diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index a5d19b5cbf..f7b888f824 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -1,5 +1,4 @@ -using System.Linq; -using MediaBrowser.Model.Weather; +using MediaBrowser.Model.Weather; using System; namespace MediaBrowser.Model.Configuration @@ -211,7 +210,6 @@ namespace MediaBrowser.Model.Configuration public string[] ManualLoginClients { get; set; } public ChannelOptions ChannelOptions { get; set; } - public ChapterOptions ChapterOptions { get; set; } public bool DefaultMetadataSettingsApplied { get; set; } @@ -274,8 +272,6 @@ namespace MediaBrowser.Model.Configuration SubtitleOptions = new SubtitleOptions(); - ChannelOptions = new ChannelOptions(); - LiveTvOptions = new LiveTvOptions(); TvFileOrganizationOptions = new TvFileOrganizationOptions(); } diff --git a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs index b2ca97f55c..9188a22814 100644 --- a/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs +++ b/MediaBrowser.Providers/MediaInfo/AudioImageProvider.cs @@ -6,7 +6,6 @@ using MediaBrowser.Controller.Entities.Audio; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Controller.Providers; using MediaBrowser.Model.Entities; -using MediaBrowser.Model.IO; using System; using System.Collections.Concurrent; using System.Collections.Generic; @@ -24,40 +23,17 @@ namespace MediaBrowser.Providers.MediaInfo { private readonly ConcurrentDictionary _locks = new ConcurrentDictionary(); - private readonly IIsoManager _isoManager; private readonly IMediaEncoder _mediaEncoder; private readonly IServerConfigurationManager _config; private readonly IFileSystem _fileSystem; - public AudioImageProvider(IIsoManager isoManager, IMediaEncoder mediaEncoder, IServerConfigurationManager config, IFileSystem fileSystem) + public AudioImageProvider(IMediaEncoder mediaEncoder, IServerConfigurationManager config, IFileSystem fileSystem) { - _isoManager = isoManager; _mediaEncoder = mediaEncoder; _config = config; _fileSystem = fileSystem; } - /// - /// The null mount task result - /// - protected readonly Task NullMountTaskResult = Task.FromResult(null); - - /// - /// Mounts the iso if needed. - /// - /// The item. - /// The cancellation token. - /// Task{IIsoMount}. - protected Task MountIsoIfNeeded(Video item, CancellationToken cancellationToken) - { - if (item.VideoType == VideoType.Iso) - { - return _isoManager.Mount(item.Path, cancellationToken); - } - - return NullMountTaskResult; - } - public IEnumerable GetSupportedImages(IHasImages item) { return new List { ImageType.Primary }; @@ -156,7 +132,7 @@ namespace MediaBrowser.Providers.MediaInfo public bool Supports(IHasImages item) { - return item.LocationType == LocationType.FileSystem && item is Audio; + return item is Audio; } public bool HasChanged(IHasMetadata item, IDirectoryService directoryService, DateTime date) diff --git a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs index 0f2ed1642f..a856d5dba3 100644 --- a/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs +++ b/MediaBrowser.Providers/MediaInfo/FFProbeAudioInfo.cs @@ -98,7 +98,7 @@ namespace MediaBrowser.Providers.MediaInfo audio.FormatName = mediaInfo.Format; audio.TotalBitrate = mediaInfo.TotalBitrate; - audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.Video); + audio.HasEmbeddedImage = mediaStreams.Any(i => i.Type == MediaStreamType.EmbeddedImage); if (data.streams != null) { diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs b/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs new file mode 100644 index 0000000000..9dfb0404e0 --- /dev/null +++ b/MediaBrowser.Server.Implementations/Channels/ChannelConfigurations.cs @@ -0,0 +1,29 @@ +using MediaBrowser.Common.Configuration; +using MediaBrowser.Model.Configuration; +using System.Collections.Generic; + +namespace MediaBrowser.Server.Implementations.Channels +{ + public static class ChannelConfigurationExtension + { + public static ChannelOptions GetChannelsConfiguration(this IConfigurationManager manager) + { + return manager.GetConfiguration("channels"); + } + } + + public class ChannelConfigurationFactory : IConfigurationFactory + { + public IEnumerable GetConfigurations() + { + return new List + { + new ConfigurationStore + { + Key = "channels", + ConfigurationType = typeof (ChannelOptions) + } + }; + } + } +} diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs index ce5c41f890..d219ab0276 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelDownloadScheduledTask.cs @@ -146,9 +146,11 @@ namespace MediaBrowser.Server.Implementations.Channels { var numComplete = 0; + var options = _config.GetChannelsConfiguration(); + foreach (var item in result.Items) { - if (_config.Configuration.ChannelOptions.DownloadingChannels.Contains(item.ChannelId)) + if (options.DownloadingChannels.Contains(item.ChannelId)) { try { @@ -282,12 +284,14 @@ namespace MediaBrowser.Server.Implementations.Channels private void CleanChannelContent(CancellationToken cancellationToken) { - if (!_config.Configuration.ChannelOptions.MaxDownloadAge.HasValue) + var options = _config.GetChannelsConfiguration(); + + if (!options.MaxDownloadAge.HasValue) { return; } - var minDateModified = DateTime.UtcNow.AddDays(0 - _config.Configuration.ChannelOptions.MaxDownloadAge.Value); + var minDateModified = DateTime.UtcNow.AddDays(0 - options.MaxDownloadAge.Value); var path = _manager.ChannelDownloadPath; diff --git a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs index 188a26c604..c6dd807585 100644 --- a/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs +++ b/MediaBrowser.Server.Implementations/Channels/ChannelManager.cs @@ -77,9 +77,11 @@ namespace MediaBrowser.Server.Implementations.Channels { get { - if (!string.IsNullOrWhiteSpace(_config.Configuration.ChannelOptions.DownloadPath)) + var options = _config.GetChannelsConfiguration(); + + if (!string.IsNullOrWhiteSpace(options.DownloadPath)) { - return _config.Configuration.ChannelOptions.DownloadPath; + return options.DownloadPath; } return Path.Combine(_config.ApplicationPaths.ProgramDataPath, "channels"); @@ -374,7 +376,9 @@ namespace MediaBrowser.Server.Implementations.Channels { var list = channelMediaSources.ToList(); - var width = _config.Configuration.ChannelOptions.PreferredStreamingWidth; + var options = _config.GetChannelsConfiguration(); + + var width = options.PreferredStreamingWidth; if (width.HasValue) { diff --git a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj index 7410f63778..3401078518 100644 --- a/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj +++ b/MediaBrowser.Server.Implementations/MediaBrowser.Server.Implementations.csproj @@ -101,6 +101,7 @@ Properties\SharedVersion.cs + diff --git a/MediaBrowser.ServerApplication/ApplicationHost.cs b/MediaBrowser.ServerApplication/ApplicationHost.cs index 96aa9c9d59..87f18e4cba 100644 --- a/MediaBrowser.ServerApplication/ApplicationHost.cs +++ b/MediaBrowser.ServerApplication/ApplicationHost.cs @@ -310,6 +310,13 @@ namespace MediaBrowser.ServerApplication saveConfig = true; } + if (ServerConfigurationManager.Configuration.ChannelOptions != null) + { + ServerConfigurationManager.SaveConfiguration("channels", ServerConfigurationManager.Configuration.ChannelOptions); + ServerConfigurationManager.Configuration.ChannelOptions = null; + saveConfig = true; + } + if (saveConfig) { ServerConfigurationManager.SaveConfiguration();