diff --git a/Emby.Dlna/Didl/DidlBuilder.cs b/Emby.Dlna/Didl/DidlBuilder.cs index 3e47362f65..8771681ad7 100644 --- a/Emby.Dlna/Didl/DidlBuilder.cs +++ b/Emby.Dlna/Didl/DidlBuilder.cs @@ -176,6 +176,18 @@ namespace Emby.Dlna.Didl return new NullLogger(); } + private string GetMimeType(string input) + { + var mime = MimeTypes.GetMimeType(input); + + if (string.Equals(mime, "video/mp2t", StringComparison.OrdinalIgnoreCase)) + { + mime = "video/mpeg"; + } + + return mime; + } + private void AddVideoResource(DlnaOptions options, XmlWriter writer, IHasMediaSources video, string deviceId, Filter filter, StreamInfo streamInfo = null) { if (streamInfo == null) @@ -360,7 +372,7 @@ namespace Emby.Dlna.Didl var filename = url.Substring(0, url.IndexOf('?')); var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) - ? MimeTypes.GetMimeType(filename) + ? GetMimeType(filename) : mediaProfile.MimeType; writer.WriteAttributeString("protocolInfo", String.Format( @@ -481,7 +493,7 @@ namespace Emby.Dlna.Didl var filename = url.Substring(0, url.IndexOf('?')); var mimeType = mediaProfile == null || string.IsNullOrEmpty(mediaProfile.MimeType) - ? MimeTypes.GetMimeType(filename) + ? GetMimeType(filename) : mediaProfile.MimeType; var contentFeatures = new ContentFeatureBuilder(_profile).BuildAudioHeader(streamInfo.Container, @@ -760,7 +772,7 @@ namespace Emby.Dlna.Didl // Seeing some LG models locking up due content with large lists of people // The actual issue might just be due to processing a more metadata than it can handle - var limit = 10; + var limit = 6; foreach (var actor in people) { @@ -1007,7 +1019,7 @@ namespace Emby.Dlna.Didl writer.WriteAttributeString("protocolInfo", String.Format( "http-get:*:{0}:{1}", - MimeTypes.GetMimeType("file." + format), + GetMimeType("file." + format), contentFeatures )); diff --git a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs index d494a7bfca..89e0697c16 100644 --- a/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs +++ b/Emby.Dlna/Profiles/DishHopperJoeyProfile.cs @@ -23,7 +23,7 @@ namespace Emby.Dlna.Profiles { Match = HeaderMatchType.Substring, Name = "User-Agent", - Value ="XiP" + Value ="Zip_" } } }; @@ -63,22 +63,7 @@ namespace Emby.Dlna.Profiles new DirectPlayProfile { - Container = "mp3", - AudioCodec = "mp3", - Type = DlnaProfileType.Audio - }, - - new DirectPlayProfile - { - Container = "alac", - AudioCodec = "alac", - Type = DlnaProfileType.Audio - }, - - new DirectPlayProfile - { - Container = "flac", - AudioCodec = "flac", + Container = "mp3,alac,flac", Type = DlnaProfileType.Audio }, diff --git a/Emby.Dlna/Profiles/SonyPs4Profile.cs b/Emby.Dlna/Profiles/SonyPs4Profile.cs index 44649911dc..bcb47486cc 100644 --- a/Emby.Dlna/Profiles/SonyPs4Profile.cs +++ b/Emby.Dlna/Profiles/SonyPs4Profile.cs @@ -86,7 +86,9 @@ namespace Emby.Dlna.Profiles { Container = "mp3", AudioCodec = "mp3", - Type = DlnaProfileType.Audio + Type = DlnaProfileType.Audio, + // Transcoded audio won't be playable at all without this + TranscodeSeekInfo = TranscodeSeekInfo.Bytes }, new TranscodingProfile { diff --git a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml index eb63352a3f..556a3d673d 100644 --- a/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml +++ b/Emby.Dlna/Profiles/Xml/Dish Hopper-Joey.xml @@ -5,7 +5,7 @@ Echostar Technologies LLC http://www.echostar.com - + Emby @@ -37,9 +37,7 @@ - - - + diff --git a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml index 7ec4ccc907..d4eeaf80d0 100644 --- a/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml +++ b/Emby.Dlna/Profiles/Xml/Sony PlayStation 4.xml @@ -45,7 +45,7 @@ - + diff --git a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs index b75de2ff4f..4fbcf0042c 100644 --- a/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs +++ b/Emby.Server.Core/EntryPoints/ExternalPortForwarding.cs @@ -106,6 +106,11 @@ namespace Emby.Server.Core.EntryPoints private async void _deviceDiscovery_DeviceDiscovered(object sender, GenericEventArgs e) { + if (_disposed) + { + return; + } + var info = e.Argument; string usn; @@ -169,6 +174,11 @@ namespace Emby.Server.Core.EntryPoints return; } + if (_disposed) + { + return; + } + _logger.Debug("Calling Nat.Handle on " + identifier); NatUtility.Handle(localAddress, info, endpoint, NatProtocol.Upnp); } @@ -185,6 +195,11 @@ namespace Emby.Server.Core.EntryPoints void NatUtility_DeviceFound(object sender, DeviceEventArgs e) { + if (_disposed) + { + return; + } + try { var device = e.Device; @@ -210,6 +225,11 @@ namespace Emby.Server.Core.EntryPoints private List _usnsHandled = new List(); private void CreateRules(INatDevice device) { + if (_disposed) + { + throw new ObjectDisposedException("PortMapper"); + } + // On some systems the device discovered event seems to fire repeatedly // This check will help ensure we're not trying to port map the same device over and over @@ -249,8 +269,10 @@ namespace Emby.Server.Core.EntryPoints _logger.Debug("NAT device lost: {0}", device.LocalAddress.ToString()); } + private bool _disposed = false; public void Dispose() { + _disposed = true; DisposeNat(); } diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs index 6ed096f441..87b70d0479 100644 --- a/Emby.Server.Core/IO/LibraryMonitor.cs +++ b/Emby.Server.Core/IO/LibraryMonitor.cs @@ -288,6 +288,13 @@ namespace Emby.Server.Core.IO { try { + if (!_fileSystem.DirectoryExists(path)) + { + // Seeing a crash in the mono runtime due to an exception being thrown on a different thread + Logger.Info("Skipping realtime monitor for {0} because the path does not exist", path); + return; + } + var newWatcher = new FileSystemWatcher(path, "*") { IncludeSubdirectories = true diff --git a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs index 170ef07f31..7f15f20cb8 100644 --- a/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs +++ b/Emby.Server.Implementations/Connect/ConnectEntryPoint.cs @@ -9,6 +9,7 @@ using System; using System.IO; using System.Text; using System.Threading.Tasks; +using MediaBrowser.Controller.Security; using MediaBrowser.Model.IO; using MediaBrowser.Model.Threading; @@ -26,8 +27,9 @@ namespace Emby.Server.Implementations.Connect private readonly IApplicationHost _appHost; private readonly IFileSystem _fileSystem; private readonly ITimerFactory _timerFactory; + private readonly IEncryptionManager _encryption; - public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory) + public ConnectEntryPoint(IHttpClient httpClient, IApplicationPaths appPaths, ILogger logger, INetworkManager networkManager, IConnectManager connectManager, IApplicationHost appHost, IFileSystem fileSystem, ITimerFactory timerFactory, IEncryptionManager encryption) { _httpClient = httpClient; _appPaths = appPaths; @@ -37,6 +39,7 @@ namespace Emby.Server.Implementations.Connect _appHost = appHost; _fileSystem = fileSystem; _timerFactory = timerFactory; + _encryption = encryption; } public void Run() @@ -143,7 +146,7 @@ namespace Emby.Server.Implementations.Connect private string CacheFilePath { - get { return Path.Combine(_appPaths.DataPath, "wan.txt"); } + get { return Path.Combine(_appPaths.DataPath, "wan.dat"); } } private void CacheAddress(IpAddressInfo address) @@ -153,7 +156,14 @@ namespace Emby.Server.Implementations.Connect try { _fileSystem.CreateDirectory(Path.GetDirectoryName(path)); - _fileSystem.WriteAllText(path, address.ToString(), Encoding.UTF8); + } + catch (Exception ex) + { + } + + try + { + _fileSystem.WriteAllText(path, _encryption.EncryptString(address.ToString()), Encoding.UTF8); } catch (Exception ex) { @@ -169,7 +179,7 @@ namespace Emby.Server.Implementations.Connect try { - var endpoint = _fileSystem.ReadAllText(path, Encoding.UTF8); + var endpoint = _encryption.DecryptString(_fileSystem.ReadAllText(path, Encoding.UTF8)); IpAddressInfo ipAddress; if (_networkManager.TryParseIpAddress(endpoint, out ipAddress)) diff --git a/Emby.Server.Implementations/Data/SqliteItemRepository.cs b/Emby.Server.Implementations/Data/SqliteItemRepository.cs index 8c16216b95..a6119f1556 100644 --- a/Emby.Server.Implementations/Data/SqliteItemRepository.cs +++ b/Emby.Server.Implementations/Data/SqliteItemRepository.cs @@ -2548,57 +2548,53 @@ namespace Emby.Server.Implementations.Data { using (var connection = CreateConnection(true)) { - return connection.RunInTransaction(db => + var list = new List(); + + using (var statement = PrepareStatementSafe(connection, commandText)) { - var list = new List(); - - using (var statement = PrepareStatementSafe(db, commandText)) + if (EnableJoinUserData(query)) { - if (EnableJoinUserData(query)) + statement.TryBind("@UserId", query.User.Id); + } + + BindSimilarParams(query, statement); + + // Running this again will bind the params + GetWhereClauses(query, statement); + + foreach (var row in statement.ExecuteQuery()) + { + var item = GetItem(row, query); + if (item != null) { - statement.TryBind("@UserId", query.User.Id); + list.Add(item); } + } + } - BindSimilarParams(query, statement); + // Hack for right now since we currently don't support filtering out these duplicates within a query + if (query.EnableGroupByMetadataKey) + { + var limit = query.Limit ?? int.MaxValue; + limit -= 4; + var newList = new List(); - // Running this again will bind the params - GetWhereClauses(query, statement); + foreach (var item in list) + { + AddItem(newList, item); - foreach (var row in statement.ExecuteQuery()) + if (newList.Count >= limit) { - var item = GetItem(row, query); - if (item != null) - { - list.Add(item); - } + break; } } - // Hack for right now since we currently don't support filtering out these duplicates within a query - if (query.EnableGroupByMetadataKey) - { - var limit = query.Limit ?? int.MaxValue; - limit -= 4; - var newList = new List(); + list = newList; + } - foreach (var item in list) - { - AddItem(newList, item); + LogQueryTime("GetItemList", commandText, now); - if (newList.Count >= limit) - { - break; - } - } - - list = newList; - } - - LogQueryTime("GetItemList", commandText, now); - - return list; - - }, ReadTransactionMode); + return list; } } } diff --git a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs index e2446b16ff..46b9142328 100644 --- a/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs +++ b/Emby.Server.Implementations/LiveTv/Listings/SchedulesDirect.cs @@ -182,7 +182,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings programsInfo.Add(GetProgram(channelNumber, schedule, programDict[schedule.programID])); } - _logger.Info("Finished with EPGData"); } } @@ -322,8 +321,7 @@ namespace Emby.Server.Implementations.LiveTv.Listings using (var response = await Get(httpOptions, true, info).ConfigureAwait(false)) { var root = _jsonSerializer.DeserializeFromStream(response); - _logger.Info("Found " + root.map.Count + " channels on the lineup on ScheduleDirect"); - _logger.Info("Mapping Stations to Channel"); + foreach (ScheduleDirect.Map map in root.map) { var channelNumber = map.logicalChannelNumber; @@ -353,7 +351,6 @@ namespace Emby.Server.Implementations.LiveTv.Listings }); } } - _logger.Info("Added " + GetChannelPairCacheCount(listingsId) + " channels to the dictionary"); foreach (ChannelInfo channel in channels) { diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 77efe8585b..c2abf1d34f 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -103,7 +103,8 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun TunerHostId = info.Id, IsHD = i.HD == 1, AudioCodec = i.AudioCodec, - VideoCodec = i.VideoCodec + VideoCodec = i.VideoCodec, + ChannelType = ChannelType.TV }); } diff --git a/Emby.Server.Implementations/Sync/SyncHelper.cs b/Emby.Server.Implementations/Sync/SyncHelper.cs index da475f0038..7fe703796f 100644 --- a/Emby.Server.Implementations/Sync/SyncHelper.cs +++ b/Emby.Server.Implementations/Sync/SyncHelper.cs @@ -4,7 +4,7 @@ namespace Emby.Server.Implementations.Sync { public class SyncHelper { - public static int? AdjustBitrate(int? profileBitrate, string quality) + public static long? AdjustBitrate(long? profileBitrate, string quality) { if (profileBitrate.HasValue) { diff --git a/Emby.Server.Implementations/Sync/SyncJobProcessor.cs b/Emby.Server.Implementations/Sync/SyncJobProcessor.cs index b1adc64df6..17cdef5fcd 100644 --- a/Emby.Server.Implementations/Sync/SyncJobProcessor.cs +++ b/Emby.Server.Implementations/Sync/SyncJobProcessor.cs @@ -59,15 +59,8 @@ namespace Emby.Server.Implementations.Sync _mediaSourceManager = mediaSourceManager; } - public async Task EnsureJobItems(SyncJob job) + public async Task EnsureJobItems(SyncJob job, User user) { - var user = _userManager.GetUserById(job.UserId); - - if (user == null) - { - throw new InvalidOperationException("Cannot proceed with sync because user no longer exists."); - } - var items = (await GetItemsForSync(job.Category, job.ParentId, job.RequestedItemIds, user, job.UnwatchedOnly).ConfigureAwait(false)) .ToList(); @@ -385,7 +378,16 @@ namespace Emby.Server.Implementations.Sync if (job.SyncNewContent) { - await EnsureJobItems(job).ConfigureAwait(false); + var user = _userManager.GetUserById(job.UserId); + + if (user == null) + { + await _syncManager.CancelJob(job.Id).ConfigureAwait(false); + } + else + { + await EnsureJobItems(job, user).ConfigureAwait(false); + } } } } diff --git a/Emby.Server.Implementations/Sync/SyncManager.cs b/Emby.Server.Implementations/Sync/SyncManager.cs index 310b35afe3..2687eaefc1 100644 --- a/Emby.Server.Implementations/Sync/SyncManager.cs +++ b/Emby.Server.Implementations/Sync/SyncManager.cs @@ -181,7 +181,7 @@ namespace Emby.Server.Implementations.Sync await _repo.Create(job).ConfigureAwait(false); - await processor.EnsureJobItems(job).ConfigureAwait(false); + await processor.EnsureJobItems(job, user).ConfigureAwait(false); // If it already has a converting status then is must have been aborted during conversion var jobItemsResult = GetJobItems(new SyncJobItemQuery diff --git a/MediaBrowser.Api/Playback/BaseStreamingService.cs b/MediaBrowser.Api/Playback/BaseStreamingService.cs index dc137521c8..1ad4720386 100644 --- a/MediaBrowser.Api/Playback/BaseStreamingService.cs +++ b/MediaBrowser.Api/Playback/BaseStreamingService.cs @@ -205,7 +205,8 @@ namespace MediaBrowser.Api.Playback } else { - args += "-map -0:v"; + // No known video stream + args += "-vn"; } if (state.AudioStream != null) @@ -395,8 +396,6 @@ namespace MediaBrowser.Api.Playback { param += " -crf 23"; } - - param += " -tune zerolatency"; } else if (string.Equals(videoEncoder, "libx265", StringComparison.OrdinalIgnoreCase)) @@ -536,6 +535,11 @@ namespace MediaBrowser.Api.Playback } } + if (string.Equals(videoEncoder, "libx264", StringComparison.OrdinalIgnoreCase)) + { + param += " -x264opts:0 subme=0:rc_lookahead=10:me_range=4:me=dia:no_chroma_me:8x8dct=0:partitions=none"; + } + if (!string.Equals(videoEncoder, "h264_omx", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_qsv", StringComparison.OrdinalIgnoreCase) && !string.Equals(videoEncoder, "h264_vaapi", StringComparison.OrdinalIgnoreCase)) @@ -1492,8 +1496,16 @@ namespace MediaBrowser.Api.Playback return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } + if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + { + // h264 + return string.Format(" -maxrate {0} -bufsize {1}", + bitrate.Value.ToString(UsCulture), + (bitrate.Value * 2).ToString(UsCulture)); + } + // h264 - return string.Format(" -maxrate {0} -bufsize {1}", + return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}", bitrate.Value.ToString(UsCulture), (bitrate.Value * 2).ToString(UsCulture)); } diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 17a0eeb8ef..299cb33fa6 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -103,7 +103,7 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - var waitForSegments = state.SegmentLength >= 10 ? 2 : (state.SegmentLength > 3 || !isLive ? 3 : 3); + var waitForSegments = state.SegmentLength >= 10 ? 2 : 3; await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false); } } diff --git a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs index 171c971f1b..cd8d05996b 100644 --- a/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/DynamicHlsService.cs @@ -265,9 +265,15 @@ namespace MediaBrowser.Api.Playback.Hls double startSeconds = 0; var lengths = GetSegmentLengths(state); + if (requestedIndex >= lengths.Length) + { + var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length); + throw new ArgumentException(msg); + } + for (var i = 0; i < requestedIndex; i++) { - startSeconds += lengths[requestedIndex]; + startSeconds += lengths[i]; } var position = TimeSpan.FromSeconds(startSeconds).Ticks; @@ -279,9 +285,15 @@ namespace MediaBrowser.Api.Playback.Hls double startSeconds = 0; var lengths = GetSegmentLengths(state); + if (requestedIndex >= lengths.Length) + { + var msg = string.Format("Invalid segment index requested: {0} - Segment count: {1}", requestedIndex, lengths.Length); + throw new ArgumentException(msg); + } + for (var i = 0; i <= requestedIndex; i++) { - startSeconds += lengths[requestedIndex]; + startSeconds += lengths[i]; } var position = TimeSpan.FromSeconds(startSeconds).Ticks; @@ -348,6 +360,8 @@ namespace MediaBrowser.Api.Playback.Hls return; } + Logger.Debug("Deleting partial HLS file {0}", file.FullName); + try { FileSystem.DeleteFile(file.FullName); @@ -723,7 +737,7 @@ namespace MediaBrowser.Api.Playback.Hls foreach (var length in segmentLengths) { - builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ","); + builder.AppendLine("#EXTINF:" + length.ToString("0.0000", UsCulture) + ", nodesc"); builder.AppendLine(string.Format("hls1/{0}/{1}{2}{3}", @@ -826,7 +840,7 @@ namespace MediaBrowser.Api.Playback.Hls args += " -bsf:v h264_mp4toannexb"; } - args += " -flags -global_header"; + //args += " -flags -global_header"; } else { @@ -851,7 +865,7 @@ namespace MediaBrowser.Api.Playback.Hls args += GetGraphicalSubtitleParam(state, codec); } - args += " -flags -global_header"; + //args += " -flags -global_header"; } if (EnableCopyTs(state) && args.IndexOf("-copyts", StringComparison.OrdinalIgnoreCase) == -1) @@ -875,27 +889,25 @@ namespace MediaBrowser.Api.Playback.Hls var inputModifier = GetInputModifier(state, false); // If isEncoding is true we're actually starting ffmpeg - var startNumberParam = isEncoding ? GetStartNumber(state).ToString(UsCulture) : "0"; - - var toTimeParam = string.Empty; - var timestampOffsetParam = string.Empty; - - if (state.IsOutputVideo && !EnableCopyTs(state) && !string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && (state.Request.StartTimeTicks ?? 0) > 0) - { - timestampOffsetParam = " -output_ts_offset " + MediaEncoder.GetTimeParameter(state.Request.StartTimeTicks ?? 0); - } + var startNumber = GetStartNumber(state); + var startNumberParam = isEncoding ? startNumber.ToString(UsCulture) : "0"; var mapArgs = state.IsOutputVideo ? GetMapArgs(state) : string.Empty; - var enableSplittingOnNonKeyFrames = string.Equals(state.OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase) && false; + var useGenericSegmenter = true; - // TODO: check libavformat version for 57 50.100 and use -hls_flags split_by_time - var hlsProtocolSupportsSplittingByTime = false; - - if (enableSplittingOnNonKeyFrames && !hlsProtocolSupportsSplittingByTime) + if (useGenericSegmenter) { var outputTsArg = Path.Combine(Path.GetDirectoryName(outputPath), Path.GetFileNameWithoutExtension(outputPath)) + "%d" + GetSegmentFileExtension(state); - return string.Format("{0} {10} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} -break_non_keyframes 1 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", + var timeDeltaParam = String.Empty; + + if (isEncoding && startNumber > 0) + { + var startTime = state.SegmentLength * startNumber; + timeDeltaParam = string.Format("-segment_time_delta -{0}", startTime); + } + + return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -f segment -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -segment_time {6} {10} -individual_header_trailer 0 -segment_format mpegts -segment_list_type m3u8 -segment_start_number {7} -segment_list \"{8}\" -y \"{9}\"", inputModifier, GetInputArgument(state), threads, @@ -906,27 +918,21 @@ namespace MediaBrowser.Api.Playback.Hls startNumberParam, outputPath, outputTsArg, - toTimeParam - ).Trim(); + timeDeltaParam + ).Trim(); } - var splitByTime = hlsProtocolSupportsSplittingByTime && enableSplittingOnNonKeyFrames; - var splitByTimeArg = splitByTime ? " -hls_flags split_by_time" : ""; - - return string.Format("{0}{12} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4}{5} {6} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {7}{8} -start_number {9} -hls_list_size {10} -y \"{11}\"", + return string.Format("{0} {1} -map_metadata -1 -map_chapters -1 -threads {2} {3} {4} {5} -max_delay 5000000 -avoid_negative_ts disabled -start_at_zero -hls_time {6} -individual_header_trailer 0 -start_number {7} -hls_list_size {8} -y \"{9}\"", inputModifier, GetInputArgument(state), threads, mapArgs, GetVideoArguments(state), - timestampOffsetParam, GetAudioArguments(state), state.SegmentLength.ToString(UsCulture), - splitByTimeArg, startNumberParam, state.HlsListSize.ToString(UsCulture), - outputPath, - toTimeParam + outputPath ).Trim(); } diff --git a/MediaBrowser.Api/Playback/MediaInfoService.cs b/MediaBrowser.Api/Playback/MediaInfoService.cs index 8fb78b6e5d..a2ec747be0 100644 --- a/MediaBrowser.Api/Playback/MediaInfoService.cs +++ b/MediaBrowser.Api/Playback/MediaInfoService.cs @@ -227,7 +227,7 @@ namespace MediaBrowser.Api.Playback PlaybackInfoResponse result, DeviceProfile profile, AuthorizationInfo auth, - int? maxBitrate, + long? maxBitrate, long startTimeTicks, string mediaSourceId, int? audioStreamIndex, @@ -249,7 +249,7 @@ namespace MediaBrowser.Api.Playback MediaSourceInfo mediaSource, DeviceProfile profile, AuthorizationInfo auth, - int? maxBitrate, + long? maxBitrate, long startTimeTicks, string mediaSourceId, int? audioStreamIndex, @@ -383,7 +383,7 @@ namespace MediaBrowser.Api.Playback } } - private int? GetMaxBitrate(int? clientMaxBitrate) + private long? GetMaxBitrate(long? clientMaxBitrate) { var maxBitrate = clientMaxBitrate; var remoteClientMaxBitrate = _config.Configuration.RemoteClientBitrateLimit; @@ -425,7 +425,7 @@ namespace MediaBrowser.Api.Playback } } - private void SortMediaSources(PlaybackInfoResponse result, int? maxBitrate) + private void SortMediaSources(PlaybackInfoResponse result, long? maxBitrate) { var originalList = result.MediaSources.ToList(); diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index d1ef996aea..e9e3c33c0e 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -93,13 +93,9 @@ namespace MediaBrowser.Api.Playback { return 3; } - return 6; - } - - if (!RunTimeTicks.HasValue) - { return 3; } + return 3; } } diff --git a/MediaBrowser.Api/Session/SessionsService.cs b/MediaBrowser.Api/Session/SessionsService.cs index 9c107bdff0..a424412684 100644 --- a/MediaBrowser.Api/Session/SessionsService.cs +++ b/MediaBrowser.Api/Session/SessionsService.cs @@ -243,6 +243,8 @@ namespace MediaBrowser.Api.Session [ApiMember(Name = "SupportsPersistentIdentifier", Description = "Determines whether the device supports a unique identifier.", IsRequired = false, DataType = "bool", ParameterType = "query", Verb = "POST")] public bool SupportsPersistentIdentifier { get; set; } + public bool SupportsContentUploading { get; set; } + public PostCapabilities() { SupportsPersistentIdentifier = true; @@ -559,6 +561,8 @@ namespace MediaBrowser.Api.Session SupportsSync = request.SupportsSync, + SupportsContentUploading = request.SupportsContentUploading, + SupportsPersistentIdentifier = request.SupportsPersistentIdentifier }); } diff --git a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs index 80bbc87e38..7654ec1d76 100644 --- a/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs +++ b/MediaBrowser.MediaEncoding/Encoder/BaseEncoder.cs @@ -827,8 +827,16 @@ namespace MediaBrowser.MediaEncoding.Encoder return string.Format(" -b:v {0}", bitrate.Value.ToString(UsCulture)); } + if (string.Equals(videoCodec, "libx264", StringComparison.OrdinalIgnoreCase)) + { + // h264 + return string.Format(" -maxrate {0} -bufsize {1}", + bitrate.Value.ToString(UsCulture), + (bitrate.Value * 2).ToString(UsCulture)); + } + // h264 - return string.Format(" -maxrate {0} -bufsize {1}", + return string.Format(" -b:v {0} -maxrate {0} -bufsize {1}", bitrate.Value.ToString(UsCulture), (bitrate.Value * 2).ToString(UsCulture)); } @@ -896,7 +904,8 @@ namespace MediaBrowser.MediaEncoding.Encoder } else { - args += "-map -0:v"; + // No known video stream + args += "-vn"; } if (state.AudioStream != null) diff --git a/MediaBrowser.Model/Dlna/AudioOptions.cs b/MediaBrowser.Model/Dlna/AudioOptions.cs index f3b6df861d..24c7aef98b 100644 --- a/MediaBrowser.Model/Dlna/AudioOptions.cs +++ b/MediaBrowser.Model/Dlna/AudioOptions.cs @@ -41,7 +41,7 @@ namespace MediaBrowser.Model.Dlna /// /// The application's configured quality setting /// - public int? MaxBitrate { get; set; } + public long? MaxBitrate { get; set; } /// /// Gets or sets the context. @@ -59,7 +59,7 @@ namespace MediaBrowser.Model.Dlna /// Gets the maximum bitrate. /// /// System.Nullable<System.Int32>. - public int? GetMaxBitrate(bool isAudio) + public long? GetMaxBitrate(bool isAudio) { if (MaxBitrate.HasValue) { diff --git a/MediaBrowser.Model/Dlna/DeviceProfile.cs b/MediaBrowser.Model/Dlna/DeviceProfile.cs index 821531ed06..d99a21e6d1 100644 --- a/MediaBrowser.Model/Dlna/DeviceProfile.cs +++ b/MediaBrowser.Model/Dlna/DeviceProfile.cs @@ -51,8 +51,8 @@ namespace MediaBrowser.Model.Dlna public int? MaxIconWidth { get; set; } public int? MaxIconHeight { get; set; } - public int? MaxStreamingBitrate { get; set; } - public int? MaxStaticBitrate { get; set; } + public long? MaxStreamingBitrate { get; set; } + public long? MaxStaticBitrate { get; set; } public int? MusicStreamingTranscodingBitrate { get; set; } public int? MaxStaticMusicBitrate { get; set; } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bc9a3488f2..262964404e 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -93,7 +93,7 @@ namespace MediaBrowser.Model.Dlna return GetOptimalStream(streams, options.GetMaxBitrate(false)); } - private StreamInfo GetOptimalStream(List streams, int? maxBitrate) + private StreamInfo GetOptimalStream(List streams, long? maxBitrate) { streams = StreamInfoSorter.SortMediaSources(streams, maxBitrate); @@ -277,25 +277,26 @@ namespace MediaBrowser.Model.Dlna playlistItem.MaxAudioChannels = Math.Min(options.MaxAudioChannels.Value, currentValue); } - int transcodingBitrate = options.AudioTranscodingBitrate ?? + long transcodingBitrate = options.AudioTranscodingBitrate ?? options.Profile.MusicStreamingTranscodingBitrate ?? 128000; - int? configuredBitrate = options.GetMaxBitrate(true); + var configuredBitrate = options.GetMaxBitrate(true); if (configuredBitrate.HasValue) { transcodingBitrate = Math.Min(configuredBitrate.Value, transcodingBitrate); } - playlistItem.AudioBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate); + var longBitrate = Math.Min(transcodingBitrate, playlistItem.AudioBitrate ?? transcodingBitrate); + playlistItem.AudioBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate); } return playlistItem; } - private int? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio) + private long? GetBitrateForDirectPlayCheck(MediaSourceInfo item, AudioOptions options, bool isAudio) { if (item.Protocol == MediaProtocol.File) { @@ -583,11 +584,11 @@ namespace MediaBrowser.Model.Dlna int audioBitrate = GetAudioBitrate(playlistItem.SubProtocol, options.GetMaxBitrate(false), playlistItem.TargetAudioChannels, playlistItem.TargetAudioCodec, audioStream); playlistItem.AudioBitrate = Math.Min(playlistItem.AudioBitrate ?? audioBitrate, audioBitrate); - int? maxBitrateSetting = options.GetMaxBitrate(false); + var maxBitrateSetting = options.GetMaxBitrate(false); // Honor max rate if (maxBitrateSetting.HasValue) { - int videoBitrate = maxBitrateSetting.Value; + var videoBitrate = maxBitrateSetting.Value; if (playlistItem.AudioBitrate.HasValue) { @@ -595,15 +596,16 @@ namespace MediaBrowser.Model.Dlna } // Make sure the video bitrate is lower than bitrate settings but at least 64k - int currentValue = playlistItem.VideoBitrate ?? videoBitrate; - playlistItem.VideoBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000); + long currentValue = playlistItem.VideoBitrate ?? videoBitrate; + var longBitrate = Math.Max(Math.Min(videoBitrate, currentValue), 64000); + playlistItem.VideoBitrate = longBitrate > int.MaxValue ? int.MaxValue : Convert.ToInt32(longBitrate); } } return playlistItem; } - private int GetAudioBitrate(string subProtocol, int? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream) + private int GetAudioBitrate(string subProtocol, long? maxTotalBitrate, int? targetAudioChannels, string targetAudioCodec, MediaStream audioStream) { int defaultBitrate = audioStream == null ? 192000 : audioStream.BitRate ?? 192000; // Reduce the bitrate if we're downmixing @@ -865,7 +867,7 @@ namespace MediaBrowser.Model.Dlna } private bool IsEligibleForDirectPlay(MediaSourceInfo item, - int? maxBitrate, + long? maxBitrate, MediaStream subtitleStream, VideoOptions options, PlayMethod playMethod) @@ -960,7 +962,7 @@ namespace MediaBrowser.Model.Dlna return null; } - private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, int? maxBitrate) + private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, long? maxBitrate) { if (!maxBitrate.HasValue) { diff --git a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs index 293054e5b3..425fb9c93e 100644 --- a/MediaBrowser.Model/Dlna/StreamInfoSorter.cs +++ b/MediaBrowser.Model/Dlna/StreamInfoSorter.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Model.Dlna { public class StreamInfoSorter { - public static List SortMediaSources(List streams, int? maxBitrate) + public static List SortMediaSources(List streams, long? maxBitrate) { return streams.OrderBy(i => { diff --git a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs index b6e2a96aa0..913967352f 100644 --- a/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs +++ b/MediaBrowser.Model/MediaInfo/LiveStreamRequest.cs @@ -7,7 +7,7 @@ namespace MediaBrowser.Model.MediaInfo public string OpenToken { get; set; } public string UserId { get; set; } public string PlaySessionId { get; set; } - public int? MaxStreamingBitrate { get; set; } + public long? MaxStreamingBitrate { get; set; } public long? StartTimeTicks { get; set; } public int? AudioStreamIndex { get; set; } public int? SubtitleStreamIndex { get; set; } diff --git a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs index a2b85d121e..fc4714be36 100644 --- a/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs +++ b/MediaBrowser.Model/MediaInfo/PlaybackInfoRequest.cs @@ -8,7 +8,7 @@ namespace MediaBrowser.Model.MediaInfo public string UserId { get; set; } - public int? MaxStreamingBitrate { get; set; } + public long? MaxStreamingBitrate { get; set; } public long? StartTimeTicks { get; set; } diff --git a/MediaBrowser.Model/Session/ClientCapabilities.cs b/MediaBrowser.Model/Session/ClientCapabilities.cs index d5e54ae11d..222c1bd64b 100644 --- a/MediaBrowser.Model/Session/ClientCapabilities.cs +++ b/MediaBrowser.Model/Session/ClientCapabilities.cs @@ -10,7 +10,7 @@ namespace MediaBrowser.Model.Session public List SupportedCommands { get; set; } public bool SupportsMediaControl { get; set; } - + public bool SupportsContentUploading { get; set; } public string MessageCallbackUrl { get; set; } public bool SupportsPersistentIdentifier { get; set; } diff --git a/MediaBrowser.Model/Session/GeneralCommandType.cs b/MediaBrowser.Model/Session/GeneralCommandType.cs index 64b61678e2..6cceb162a4 100644 --- a/MediaBrowser.Model/Session/GeneralCommandType.cs +++ b/MediaBrowser.Model/Session/GeneralCommandType.cs @@ -36,6 +36,7 @@ DisplayMessage = 28, SetRepeatMode = 29, ChannelUp = 30, - ChannelDown = 31 + ChannelDown = 31, + SetMaxStreamingBitrate = 31 } } \ No newline at end of file diff --git a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs index 22e7e753ca..1fd04070ba 100644 --- a/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs +++ b/MediaBrowser.Providers/TV/MissingEpisodeProvider.cs @@ -93,6 +93,12 @@ namespace MediaBrowser.Providers.TV return; } + // Check this in order to avoid logging an exception due to directory not existing + if (!_fileSystem.DirectoryExists(seriesDataPath)) + { + return; + } + var episodeFiles = _fileSystem.GetFilePaths(seriesDataPath) .Where(i => string.Equals(Path.GetExtension(i), ".xml", StringComparison.OrdinalIgnoreCase)) .Select(Path.GetFileNameWithoutExtension) diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs index 8488c56694..f6af365fdb 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbPrescanTask.cs @@ -143,7 +143,8 @@ namespace MediaBrowser.Providers.TV Url = ServerTimeUrl, CancellationToken = cancellationToken, EnableHttpCompression = true, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool, + BufferContent = false }).ConfigureAwait(false)) { @@ -239,7 +240,8 @@ namespace MediaBrowser.Providers.TV Url = string.Format(UpdatesUrl, lastUpdateTime), CancellationToken = cancellationToken, EnableHttpCompression = true, - ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool + ResourcePool = TvdbSeriesProvider.Current.TvDbResourcePool, + BufferContent = false }).ConfigureAwait(false)) { diff --git a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs index cc8a90fe38..1798299e8d 100644 --- a/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs +++ b/MediaBrowser.Providers/TV/TheTVDB/TvdbSeriesProvider.cs @@ -237,7 +237,8 @@ namespace MediaBrowser.Providers.TV { Url = url, ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = false }).ConfigureAwait(false)) { @@ -281,7 +282,8 @@ namespace MediaBrowser.Providers.TV { Url = url, ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = false }).ConfigureAwait(false)) { @@ -535,7 +537,8 @@ namespace MediaBrowser.Providers.TV { Url = url, ResourcePool = TvDbResourcePool, - CancellationToken = cancellationToken + CancellationToken = cancellationToken, + BufferContent = false }).ConfigureAwait(false)) { @@ -1664,7 +1667,8 @@ namespace MediaBrowser.Providers.TV { CancellationToken = cancellationToken, Url = url, - ResourcePool = TvDbResourcePool + ResourcePool = TvDbResourcePool, + BufferContent = false }); } } diff --git a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj index 37ee31df1f..ea2b299383 100644 --- a/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj +++ b/MediaBrowser.WebDashboard/MediaBrowser.WebDashboard.csproj @@ -162,9 +162,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -258,6 +255,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -420,9 +420,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -648,9 +645,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -816,9 +810,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -903,9 +894,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -978,6 +966,9 @@ PreserveNewest + + PreserveNewest + PreserveNewest @@ -1027,9 +1018,6 @@ PreserveNewest - - PreserveNewest - PreserveNewest @@ -1388,9 +1376,6 @@ PreserveNewest - - PreserveNewest - diff --git a/RSSDP/SsdpDevicePublisherBase.cs b/RSSDP/SsdpDevicePublisherBase.cs index 2543632b6b..c0ae3955dc 100644 --- a/RSSDP/SsdpDevicePublisherBase.cs +++ b/RSSDP/SsdpDevicePublisherBase.cs @@ -245,7 +245,7 @@ namespace Rssdp.Infrastructure return; } - WriteTrace(String.Format("Search Request Received From {0}, Target = {1}", remoteEndPoint.ToString(), searchTarget)); + //WriteTrace(String.Format("Search Request Received From {0}, Target = {1}", remoteEndPoint.ToString(), searchTarget)); if (IsDuplicateSearchRequest(searchTarget, remoteEndPoint)) {