From 38badd4d28e6538e1c33b0d714b50cb24f0f6897 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 10 Mar 2017 14:51:29 -0500 Subject: rework file system libs --- .../IO/LnkShortcutHandler.cs | 333 +++++++++++++++++++++ .../IO/ManagedFileSystem.cs | 11 +- 2 files changed, 339 insertions(+), 5 deletions(-) create mode 100644 Emby.Common.Implementations/IO/LnkShortcutHandler.cs (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/IO/LnkShortcutHandler.cs b/Emby.Common.Implementations/IO/LnkShortcutHandler.cs new file mode 100644 index 000000000..5d5f46057 --- /dev/null +++ b/Emby.Common.Implementations/IO/LnkShortcutHandler.cs @@ -0,0 +1,333 @@ +using System; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.InteropServices.ComTypes; +using System.Security; +using System.Text; +using MediaBrowser.Model.IO; + +namespace Emby.Common.Implementations.IO +{ + public class LnkShortcutHandler :IShortcutHandler + { + public string Extension + { + get { return ".lnk"; } + } + + public string Resolve(string shortcutPath) + { + var link = new ShellLink(); + ((IPersistFile)link).Load(shortcutPath, NativeMethods.STGM_READ); + // ((IShellLinkW)link).Resolve(hwnd, 0) + var sb = new StringBuilder(NativeMethods.MAX_PATH); + WIN32_FIND_DATA data; + ((IShellLinkW)link).GetPath(sb, sb.Capacity, out data, 0); + return sb.ToString(); + } + + public void Create(string shortcutPath, string targetPath) + { + throw new NotImplementedException(); + } + } + + /// + /// Class NativeMethods + /// + public static class NativeMethods + { + /// + /// The MA x_ PATH + /// + public const int MAX_PATH = 260; + /// + /// The MA x_ ALTERNATE + /// + public const int MAX_ALTERNATE = 14; + /// + /// The INVALI d_ HANDL e_ VALUE + /// + public static IntPtr INVALID_HANDLE_VALUE = new IntPtr(-1); + /// + /// The STG m_ READ + /// + public const int STGM_READ = 0; + } + + /// + /// Struct FILETIME + /// + [StructLayout(LayoutKind.Sequential)] + public struct FILETIME + { + /// + /// The dw low date time + /// + public uint dwLowDateTime; + /// + /// The dw high date time + /// + public uint dwHighDateTime; + } + + /// + /// Struct WIN32_FIND_DATA + /// + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + public struct WIN32_FIND_DATA + { + /// + /// The dw file attributes + /// + public FileAttributes dwFileAttributes; + /// + /// The ft creation time + /// + public FILETIME ftCreationTime; + /// + /// The ft last access time + /// + public FILETIME ftLastAccessTime; + /// + /// The ft last write time + /// + public FILETIME ftLastWriteTime; + /// + /// The n file size high + /// + public int nFileSizeHigh; + /// + /// The n file size low + /// + public int nFileSizeLow; + /// + /// The dw reserved0 + /// + public int dwReserved0; + /// + /// The dw reserved1 + /// + public int dwReserved1; + + /// + /// The c file name + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.MAX_PATH)] + public string cFileName; + + /// + /// This will always be null when FINDEX_INFO_LEVELS = basic + /// + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NativeMethods.MAX_ALTERNATE)] + public string cAlternate; + + /// + /// Gets or sets the path. + /// + /// The path. + public string Path { get; set; } + + /// + /// Returns a that represents this instance. + /// + /// A that represents this instance. + public override string ToString() + { + return Path ?? string.Empty; + } + } + + /// + /// Enum SLGP_FLAGS + /// + [Flags] + public enum SLGP_FLAGS + { + /// + /// Retrieves the standard short (8.3 format) file name + /// + SLGP_SHORTPATH = 0x1, + /// + /// Retrieves the Universal Naming Convention (UNC) path name of the file + /// + SLGP_UNCPRIORITY = 0x2, + /// + /// Retrieves the raw path name. A raw path is something that might not exist and may include environment variables that need to be expanded + /// + SLGP_RAWPATH = 0x4 + } + /// + /// Enum SLR_FLAGS + /// + [Flags] + public enum SLR_FLAGS + { + /// + /// Do not display a dialog box if the link cannot be resolved. When SLR_NO_UI is set, + /// the high-order word of fFlags can be set to a time-out value that specifies the + /// maximum amount of time to be spent resolving the link. The function returns if the + /// link cannot be resolved within the time-out duration. If the high-order word is set + /// to zero, the time-out duration will be set to the default value of 3,000 milliseconds + /// (3 seconds). To specify a value, set the high word of fFlags to the desired time-out + /// duration, in milliseconds. + /// + SLR_NO_UI = 0x1, + /// + /// Obsolete and no longer used + /// + SLR_ANY_MATCH = 0x2, + /// + /// If the link object has changed, update its path and list of identifiers. + /// If SLR_UPDATE is set, you do not need to call IPersistFile::IsDirty to determine + /// whether or not the link object has changed. + /// + SLR_UPDATE = 0x4, + /// + /// Do not update the link information + /// + SLR_NOUPDATE = 0x8, + /// + /// Do not execute the search heuristics + /// + SLR_NOSEARCH = 0x10, + /// + /// Do not use distributed link tracking + /// + SLR_NOTRACK = 0x20, + /// + /// Disable distributed link tracking. By default, distributed link tracking tracks + /// removable media across multiple devices based on the volume name. It also uses the + /// Universal Naming Convention (UNC) path to track remote file systems whose drive letter + /// has changed. Setting SLR_NOLINKINFO disables both types of tracking. + /// + SLR_NOLINKINFO = 0x40, + /// + /// Call the Microsoft Windows Installer + /// + SLR_INVOKE_MSI = 0x80 + } + + /// + /// The IShellLink interface allows Shell links to be created, modified, and resolved + /// + [ComImport, InterfaceType(ComInterfaceType.InterfaceIsIUnknown), Guid("000214F9-0000-0000-C000-000000000046")] + public interface IShellLinkW + { + /// + /// Retrieves the path and file name of a Shell link object + /// + /// The PSZ file. + /// The CCH max path. + /// The PFD. + /// The f flags. + void GetPath([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszFile, int cchMaxPath, out WIN32_FIND_DATA pfd, SLGP_FLAGS fFlags); + /// + /// Retrieves the list of item identifiers for a Shell link object + /// + /// The ppidl. + void GetIDList(out IntPtr ppidl); + /// + /// Sets the pointer to an item identifier list (PIDL) for a Shell link object. + /// + /// The pidl. + void SetIDList(IntPtr pidl); + /// + /// Retrieves the description string for a Shell link object + /// + /// Name of the PSZ. + /// Name of the CCH max. + void GetDescription([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszName, int cchMaxName); + /// + /// Sets the description for a Shell link object. The description can be any application-defined string + /// + /// Name of the PSZ. + void SetDescription([MarshalAs(UnmanagedType.LPWStr)] string pszName); + /// + /// Retrieves the name of the working directory for a Shell link object + /// + /// The PSZ dir. + /// The CCH max path. + void GetWorkingDirectory([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszDir, int cchMaxPath); + /// + /// Sets the name of the working directory for a Shell link object + /// + /// The PSZ dir. + void SetWorkingDirectory([MarshalAs(UnmanagedType.LPWStr)] string pszDir); + /// + /// Retrieves the command-line arguments associated with a Shell link object + /// + /// The PSZ args. + /// The CCH max path. + void GetArguments([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszArgs, int cchMaxPath); + /// + /// Sets the command-line arguments for a Shell link object + /// + /// The PSZ args. + void SetArguments([MarshalAs(UnmanagedType.LPWStr)] string pszArgs); + /// + /// Retrieves the hot key for a Shell link object + /// + /// The pw hotkey. + void GetHotkey(out short pwHotkey); + /// + /// Sets a hot key for a Shell link object + /// + /// The w hotkey. + void SetHotkey(short wHotkey); + /// + /// Retrieves the show command for a Shell link object + /// + /// The pi show CMD. + void GetShowCmd(out int piShowCmd); + /// + /// Sets the show command for a Shell link object. The show command sets the initial show state of the window. + /// + /// The i show CMD. + void SetShowCmd(int iShowCmd); + /// + /// Retrieves the location (path and index) of the icon for a Shell link object + /// + /// The PSZ icon path. + /// The CCH icon path. + /// The pi icon. + void GetIconLocation([Out, MarshalAs(UnmanagedType.LPWStr)] StringBuilder pszIconPath, + int cchIconPath, out int piIcon); + /// + /// Sets the location (path and index) of the icon for a Shell link object + /// + /// The PSZ icon path. + /// The i icon. + void SetIconLocation([MarshalAs(UnmanagedType.LPWStr)] string pszIconPath, int iIcon); + /// + /// Sets the relative path to the Shell link object + /// + /// The PSZ path rel. + /// The dw reserved. + void SetRelativePath([MarshalAs(UnmanagedType.LPWStr)] string pszPathRel, int dwReserved); + /// + /// Attempts to find the target of a Shell link, even if it has been moved or renamed + /// + /// The HWND. + /// The f flags. + void Resolve(IntPtr hwnd, SLR_FLAGS fFlags); + /// + /// Sets the path and file name of a Shell link object + /// + /// The PSZ file. + void SetPath([MarshalAs(UnmanagedType.LPWStr)] string pszFile); + + } + + // CLSID_ShellLink from ShlGuid.h + /// + /// Class ShellLink + /// + [ + ComImport, + Guid("00021401-0000-0000-C000-000000000046") + ] + public class ShellLink + { + } +} diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs index 3fe20f659..2c9388a41 100644 --- a/Emby.Common.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Text; using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.System; namespace Emby.Common.Implementations.IO { @@ -18,17 +19,17 @@ namespace Emby.Common.Implementations.IO private readonly bool _supportsAsyncFileStreams; private char[] _invalidFileNameChars; private readonly List _shortcutHandlers = new List(); - private bool EnableFileSystemRequestConcat = true; + private bool EnableFileSystemRequestConcat; private string _tempPath; - public ManagedFileSystem(ILogger logger, bool supportsAsyncFileStreams, bool enableManagedInvalidFileNameChars, bool enableFileSystemRequestConcat, string tempPath) + public ManagedFileSystem(ILogger logger, IEnvironmentInfo environmentInfo, string tempPath) { Logger = logger; - _supportsAsyncFileStreams = supportsAsyncFileStreams; + _supportsAsyncFileStreams = true; _tempPath = tempPath; - EnableFileSystemRequestConcat = enableFileSystemRequestConcat; - SetInvalidFileNameChars(enableManagedInvalidFileNameChars); + EnableFileSystemRequestConcat = false; + SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows); } public void AddShortcutHandler(IShortcutHandler handler) -- cgit v1.2.3 From a660aa001eb31e91d040e066787fa764cf5f0fb4 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 12 Mar 2017 15:27:26 -0400 Subject: re-organize file streaming --- Emby.Common.Implementations/Net/NetAcceptSocket.cs | 43 ++++- Emby.Common.Implementations/Net/SocketFactory.cs | 1 + Emby.Server.Core/ApplicationHost.cs | 2 +- Emby.Server.Core/HttpServerFactory.cs | 4 +- .../Emby.Server.Implementations.csproj | 1 + .../HttpServer/FileWriter.cs | 188 +++++++++++++++++++++ .../HttpServer/HttpListenerHost.cs | 8 +- .../HttpServer/HttpResultFactory.cs | 41 +++-- .../SocketSharp/WebSocketSharpListener.cs | 6 +- .../SocketSharp/WebSocketSharpResponse.cs | 8 + .../LiveTv/EmbyTV/EmbyTV.cs | 11 ++ .../Services/ResponseHelper.cs | 46 +---- Emby.Server.Implementations/packages.config | 2 +- MediaBrowser.Controller/Entities/IHasImages.cs | 2 + MediaBrowser.Controller/Net/StaticResultOptions.cs | 13 +- MediaBrowser.Model/Net/IAcceptSocket.cs | 3 + MediaBrowser.Model/Services/IAsyncStreamWriter.cs | 5 + MediaBrowser.Model/Services/IRequest.cs | 5 +- SharedVersion.cs | 2 +- .../Net/EndPointListener.cs | 6 +- SocketHttpListener.Portable/Net/EndPointManager.cs | 3 +- SocketHttpListener.Portable/Net/HttpConnection.cs | 16 +- SocketHttpListener.Portable/Net/HttpListener.cs | 12 +- .../Net/HttpListenerContext.cs | 6 +- .../Net/HttpListenerResponse.cs | 14 +- SocketHttpListener.Portable/Net/ResponseStream.cs | 84 ++++++++- 26 files changed, 437 insertions(+), 95 deletions(-) create mode 100644 Emby.Server.Implementations/HttpServer/FileWriter.cs (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs index 731ad1b74..e21ffe553 100644 --- a/Emby.Common.Implementations/Net/NetAcceptSocket.cs +++ b/Emby.Common.Implementations/Net/NetAcceptSocket.cs @@ -2,6 +2,7 @@ using System.Net; using System.Net.Sockets; using System.Threading; +using System.Threading.Tasks; using Emby.Common.Implementations.Networking; using MediaBrowser.Model.Net; using MediaBrowser.Model.Logging; @@ -59,7 +60,7 @@ namespace Emby.Common.Implementations.Net #if NET46 Socket.Close(); #else - Socket.Dispose(); + Socket.Dispose(); #endif } @@ -96,6 +97,46 @@ namespace Emby.Common.Implementations.Net _acceptor.StartAccept(); } +#if NET46 + public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken) + { + var options = TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket | TransmitFileOptions.UseKernelApc; + + var completionSource = new TaskCompletionSource(); + + var result = Socket.BeginSendFile(path, preBuffer, postBuffer, options, new AsyncCallback(FileSendCallback), new Tuple>(Socket, path, completionSource)); + + return completionSource.Task; + } + + private void FileSendCallback(IAsyncResult ar) + { + // Retrieve the socket from the state object. + Tuple> data = (Tuple>)ar.AsyncState; + + var client = data.Item1; + var path = data.Item2; + var taskCompletion = data.Item3; + + // Complete sending the data to the remote device. + try { + client.EndSendFile(ar); + taskCompletion.TrySetResult(true); +} + catch(SocketException ex){ + _logger.Info("Socket.SendFile failed for {0}. error code {1}", path, ex.SocketErrorCode); + taskCompletion.TrySetException(ex); +}catch(Exception ex){ + taskCompletion.TrySetException(ex); +} + } +#else + public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } +#endif + public void Dispose() { Socket.Dispose(); diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index 523f4da85..021613e57 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -59,6 +59,7 @@ namespace Emby.Common.Implementations.Net if (remotePort < 0) throw new ArgumentException("remotePort cannot be less than zero.", "remotePort"); var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp); + try { retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); diff --git a/Emby.Server.Core/ApplicationHost.cs b/Emby.Server.Core/ApplicationHost.cs index 6133a3343..971378ea7 100644 --- a/Emby.Server.Core/ApplicationHost.cs +++ b/Emby.Server.Core/ApplicationHost.cs @@ -613,7 +613,7 @@ namespace Emby.Server.Core CertificatePath = GetCertificatePath(true); Certificate = GetCertificate(CertificatePath); - HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, SupportsDualModeSockets); + HttpServer = HttpServerFactory.CreateServer(this, LogManager, ServerConfigurationManager, NetworkManager, MemoryStreamFactory, "Emby", "web/index.html", textEncoding, SocketFactory, CryptographyProvider, JsonSerializer, XmlSerializer, EnvironmentInfo, Certificate, FileSystemManager, SupportsDualModeSockets); HttpServer.GlobalResponse = LocalizationManager.GetLocalizedString("StartupEmbyServerIsLoading"); RegisterSingleInstance(HttpServer, false); progress.Report(10); diff --git a/Emby.Server.Core/HttpServerFactory.cs b/Emby.Server.Core/HttpServerFactory.cs index deed3c6f3..dfd435c33 100644 --- a/Emby.Server.Core/HttpServerFactory.cs +++ b/Emby.Server.Core/HttpServerFactory.cs @@ -45,6 +45,7 @@ namespace Emby.Server.Core IXmlSerializer xml, IEnvironmentInfo environment, ICertificate certificate, + IFileSystem fileSystem, bool enableDualModeSockets) { var logger = logManager.GetLogger("HttpServer"); @@ -65,7 +66,8 @@ namespace Emby.Server.Core certificate, new StreamFactory(), GetParseFn, - enableDualModeSockets); + enableDualModeSockets, + fileSystem); } private static Func GetParseFn(Type propertyType) diff --git a/Emby.Server.Implementations/Emby.Server.Implementations.csproj b/Emby.Server.Implementations/Emby.Server.Implementations.csproj index 4e9b7c4c9..d873cd77f 100644 --- a/Emby.Server.Implementations/Emby.Server.Implementations.csproj +++ b/Emby.Server.Implementations/Emby.Server.Implementations.csproj @@ -84,6 +84,7 @@ + diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs new file mode 100644 index 000000000..b80a40962 --- /dev/null +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -0,0 +1,188 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.IO; +using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Services; + +namespace Emby.Server.Implementations.HttpServer +{ + public class FileWriter : IHttpResult + { + private ILogger Logger { get; set; } + + private string RangeHeader { get; set; } + private bool IsHeadRequest { get; set; } + + private long RangeStart { get; set; } + private long RangeEnd { get; set; } + private long RangeLength { get; set; } + private long TotalContentLength { get; set; } + + public Action OnComplete { get; set; } + public Action OnError { get; set; } + private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); + public List Cookies { get; private set; } + + /// + /// The _options + /// + private readonly IDictionary _options = new Dictionary(); + /// + /// Gets the options. + /// + /// The options. + public IDictionary Headers + { + get { return _options; } + } + + public string Path { get; set; } + + public FileWriter(string path, string contentType, string rangeHeader, ILogger logger, IFileSystem fileSystem) + { + if (string.IsNullOrEmpty(contentType)) + { + throw new ArgumentNullException("contentType"); + } + + Path = path; + Logger = logger; + RangeHeader = rangeHeader; + + Headers["Content-Type"] = contentType; + + TotalContentLength = fileSystem.GetFileInfo(path).Length; + + if (string.IsNullOrWhiteSpace(rangeHeader)) + { + Headers["Content-Length"] = TotalContentLength.ToString(UsCulture); + StatusCode = HttpStatusCode.OK; + } + else + { + Headers["Accept-Ranges"] = "bytes"; + StatusCode = HttpStatusCode.PartialContent; + SetRangeValues(); + } + + Cookies = new List(); + } + + /// + /// Sets the range values. + /// + private void SetRangeValues() + { + var requestedRange = RequestedRanges[0]; + + // If the requested range is "0-", we can optimize by just doing a stream copy + if (!requestedRange.Value.HasValue) + { + RangeEnd = TotalContentLength - 1; + } + else + { + RangeEnd = requestedRange.Value.Value; + } + + RangeStart = requestedRange.Key; + RangeLength = 1 + RangeEnd - RangeStart; + + // Content-Length is the length of what we're serving, not the original content + Headers["Content-Length"] = RangeLength.ToString(UsCulture); + Headers["Content-Range"] = string.Format("bytes {0}-{1}/{2}", RangeStart, RangeEnd, TotalContentLength); + } + + /// + /// The _requested ranges + /// + private List> _requestedRanges; + /// + /// Gets the requested ranges. + /// + /// The requested ranges. + protected List> RequestedRanges + { + get + { + if (_requestedRanges == null) + { + _requestedRanges = new List>(); + + // Example: bytes=0-,32-63 + var ranges = RangeHeader.Split('=')[1].Split(','); + + foreach (var range in ranges) + { + var vals = range.Split('-'); + + long start = 0; + long? end = null; + + if (!string.IsNullOrEmpty(vals[0])) + { + start = long.Parse(vals[0], UsCulture); + } + if (!string.IsNullOrEmpty(vals[1])) + { + end = long.Parse(vals[1], UsCulture); + } + + _requestedRanges.Add(new KeyValuePair(start, end)); + } + } + + return _requestedRanges; + } + } + + public async Task WriteToAsync(IResponse response, CancellationToken cancellationToken) + { + try + { + // Headers only + if (IsHeadRequest) + { + return; + } + + if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1)) + { + Logger.Info("Transmit file {0}", Path); + await response.TransmitFile(Path, 0, 0, cancellationToken).ConfigureAwait(false); + return; + } + + await response.TransmitFile(Path, RangeStart, RangeEnd, cancellationToken).ConfigureAwait(false); + } + finally + { + if (OnComplete != null) + { + OnComplete(); + } + } + } + + public string ContentType { get; set; } + + public IRequest RequestContext { get; set; } + + public object Response { get; set; } + + public int Status { get; set; } + + public HttpStatusCode StatusCode + { + get { return (HttpStatusCode)Status; } + set { Status = (int)value; } + } + + public string StatusDescription { get; set; } + + } +} diff --git a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs index b5a3c2992..6d15cc619 100644 --- a/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs +++ b/Emby.Server.Implementations/HttpServer/HttpListenerHost.cs @@ -52,6 +52,7 @@ namespace Emby.Server.Implementations.HttpServer private readonly ISocketFactory _socketFactory; private readonly ICryptoProvider _cryptoProvider; + private readonly IFileSystem _fileSystem; private readonly IJsonSerializer _jsonSerializer; private readonly IXmlSerializer _xmlSerializer; private readonly ICertificate _certificate; @@ -70,8 +71,7 @@ namespace Emby.Server.Implementations.HttpServer ILogger logger, IServerConfigurationManager config, string serviceName, - string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func> funcParseFn, bool enableDualModeSockets) - : base() + string defaultRedirectPath, INetworkManager networkManager, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IJsonSerializer jsonSerializer, IXmlSerializer xmlSerializer, IEnvironmentInfo environment, ICertificate certificate, IStreamFactory streamFactory, Func> funcParseFn, bool enableDualModeSockets, IFileSystem fileSystem) { Instance = this; @@ -89,6 +89,7 @@ namespace Emby.Server.Implementations.HttpServer _streamFactory = streamFactory; _funcParseFn = funcParseFn; _enableDualModeSockets = enableDualModeSockets; + _fileSystem = fileSystem; _config = config; _logger = logger; @@ -226,7 +227,8 @@ namespace Emby.Server.Implementations.HttpServer _cryptoProvider, _streamFactory, _enableDualModeSockets, - GetRequest); + GetRequest, + _fileSystem); } private IHttpRequest GetRequest(HttpListenerContext httpContext) diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index 6bfd83110..e3f105941 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -474,10 +474,6 @@ namespace Emby.Server.Implementations.HttpServer { throw new ArgumentNullException("cacheKey"); } - if (options.ContentFactory == null) - { - throw new ArgumentNullException("factoryFn"); - } var key = cacheKey.ToString("N"); @@ -560,30 +556,43 @@ namespace Emby.Server.Implementations.HttpServer { var rangeHeader = requestContext.Headers.Get("Range"); - var stream = await factoryFn().ConfigureAwait(false); + if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path) && options.FileShare == FileShareMode.Read) + { + return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) + { + OnComplete = options.OnComplete, + OnError = options.OnError + }; + } if (!string.IsNullOrEmpty(rangeHeader)) { + var stream = await factoryFn().ConfigureAwait(false); + return new RangeRequestWriter(rangeHeader, stream, contentType, isHeadRequest, _logger) { OnComplete = options.OnComplete }; } + else + { + var stream = await factoryFn().ConfigureAwait(false); - responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture); + responseHeaders["Content-Length"] = stream.Length.ToString(UsCulture); - if (isHeadRequest) - { - stream.Dispose(); + if (isHeadRequest) + { + stream.Dispose(); - return GetHttpResult(new byte[] { }, contentType, true); - } + return GetHttpResult(new byte[] { }, contentType, true); + } - return new StreamWriter(stream, contentType, _logger) - { - OnComplete = options.OnComplete, - OnError = options.OnError - }; + return new StreamWriter(stream, contentType, _logger) + { + OnComplete = options.OnComplete, + OnError = options.OnError + }; + } } using (var stream = await factoryFn().ConfigureAwait(false)) diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs index 652fc4f83..b11b2fe88 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpListener.cs @@ -27,10 +27,11 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp private readonly ISocketFactory _socketFactory; private readonly ICryptoProvider _cryptoProvider; private readonly IStreamFactory _streamFactory; + private readonly IFileSystem _fileSystem; private readonly Func _httpRequestFactory; private readonly bool _enableDualMode; - public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func httpRequestFactory) + public WebSocketSharpListener(ILogger logger, ICertificate certificate, IMemoryStreamFactory memoryStreamProvider, ITextEncoding textEncoding, INetworkManager networkManager, ISocketFactory socketFactory, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, bool enableDualMode, Func httpRequestFactory, IFileSystem fileSystem) { _logger = logger; _certificate = certificate; @@ -42,6 +43,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp _streamFactory = streamFactory; _enableDualMode = enableDualMode; _httpRequestFactory = httpRequestFactory; + _fileSystem = fileSystem; } public Action ErrorHandler { get; set; } @@ -54,7 +56,7 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp public void Start(IEnumerable urlPrefixes) { if (_listener == null) - _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider); + _listener = new HttpListener(_logger, _cryptoProvider, _streamFactory, _socketFactory, _networkManager, _textEncoding, _memoryStreamProvider, _fileSystem); _listener.EnableDualMode = _enableDualMode; diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs index 36f795411..a497ee715 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs @@ -3,6 +3,9 @@ using System.Collections.Generic; using System.IO; using System.Net; using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using SocketHttpListener.Net; using HttpListenerResponse = SocketHttpListener.Net.HttpListenerResponse; @@ -189,5 +192,10 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp public void ClearCookies() { } + + public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + { + return _response.TransmitFile(path, offset, count, cancellationToken); + } } } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index 926d82f94..d9060a066 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -1276,6 +1276,14 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV return; } + var registration = await _liveTvManager.GetRegistrationInfo("dvr").ConfigureAwait(false); + if (!registration.IsValid) + { + _logger.Warn("Emby Premiere required to use Emby DVR."); + OnTimerOutOfDate(timer); + return; + } + var activeRecordingInfo = new ActiveRecordingInfo { CancellationTokenSource = new CancellationTokenSource(), @@ -2319,6 +2327,9 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV { UpdateExistingTimerWithNewMetadata(existingTimer, timer); + // Needed by ShouldCancelTimerForSeriesTimer + timer.IsManual = existingTimer.IsManual; + if (ShouldCancelTimerForSeriesTimer(seriesTimer, timer)) { existingTimer.Status = RecordingStatus.Cancelled; diff --git a/Emby.Server.Implementations/Services/ResponseHelper.cs b/Emby.Server.Implementations/Services/ResponseHelper.cs index eea4bf049..3c2af60db 100644 --- a/Emby.Server.Implementations/Services/ResponseHelper.cs +++ b/Emby.Server.Implementations/Services/ResponseHelper.cs @@ -12,45 +12,6 @@ namespace Emby.Server.Implementations.Services { public static class ResponseHelper { - private static async Task WriteToOutputStream(IResponse response, object result) - { - var asyncStreamWriter = result as IAsyncStreamWriter; - if (asyncStreamWriter != null) - { - await asyncStreamWriter.WriteToAsync(response.OutputStream, CancellationToken.None).ConfigureAwait(false); - return true; - } - - var streamWriter = result as IStreamWriter; - if (streamWriter != null) - { - streamWriter.WriteTo(response.OutputStream); - return true; - } - - var stream = result as Stream; - if (stream != null) - { - using (stream) - { - await stream.CopyToAsync(response.OutputStream).ConfigureAwait(false); - return true; - } - } - - var bytes = result as byte[]; - if (bytes != null) - { - response.ContentType = "application/octet-stream"; - response.SetContentLength(bytes.Length); - - await response.OutputStream.WriteAsync(bytes, 0, bytes.Length).ConfigureAwait(false); - return true; - } - - return false; - } - public static Task WriteToResponse(IResponse httpRes, IRequest httpReq, object result) { if (result == null) @@ -155,6 +116,13 @@ namespace Emby.Server.Implementations.Services return; } + var fileWriter = result as FileWriter; + if (fileWriter != null) + { + await fileWriter.WriteToAsync(response, CancellationToken.None).ConfigureAwait(false); + return; + } + var stream = result as Stream; if (stream != null) { diff --git a/Emby.Server.Implementations/packages.config b/Emby.Server.Implementations/packages.config index 7e638e171..ccabbc27b 100644 --- a/Emby.Server.Implementations/packages.config +++ b/Emby.Server.Implementations/packages.config @@ -1,7 +1,7 @@  - + diff --git a/MediaBrowser.Controller/Entities/IHasImages.cs b/MediaBrowser.Controller/Entities/IHasImages.cs index 4c033dc00..2104bee09 100644 --- a/MediaBrowser.Controller/Entities/IHasImages.cs +++ b/MediaBrowser.Controller/Entities/IHasImages.cs @@ -206,6 +206,8 @@ namespace MediaBrowser.Controller.Entities void SetImage(ItemImageInfo image, int index); double? GetDefaultPrimaryImageAspectRatio(); + + int? ProductionYear { get; set; } } public static class HasImagesExtensions diff --git a/MediaBrowser.Controller/Net/StaticResultOptions.cs b/MediaBrowser.Controller/Net/StaticResultOptions.cs index 272fa8b3b..62c13fbb5 100644 --- a/MediaBrowser.Controller/Net/StaticResultOptions.cs +++ b/MediaBrowser.Controller/Net/StaticResultOptions.cs @@ -23,21 +23,18 @@ namespace MediaBrowser.Controller.Net public Action OnComplete { get; set; } public Action OnError { get; set; } + public string Path { get; set; } + + public FileShareMode FileShare { get; set; } + public StaticResultOptions() { ResponseHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); + FileShare = FileShareMode.Read; } } public class StaticFileResultOptions : StaticResultOptions { - public string Path { get; set; } - - public FileShareMode FileShare { get; set; } - - public StaticFileResultOptions() - { - FileShare = FileShareMode.Read; - } } } diff --git a/MediaBrowser.Model/Net/IAcceptSocket.cs b/MediaBrowser.Model/Net/IAcceptSocket.cs index cac23b337..4262e2390 100644 --- a/MediaBrowser.Model/Net/IAcceptSocket.cs +++ b/MediaBrowser.Model/Net/IAcceptSocket.cs @@ -1,4 +1,6 @@ using System; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Model.Net { @@ -13,6 +15,7 @@ namespace MediaBrowser.Model.Net void Bind(IpEndPointInfo endpoint); void Connect(IpEndPointInfo endPoint); void StartAccept(Action onAccept, Func isClosed); + Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken); } public class SocketCreateException : Exception diff --git a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs index b10e12813..f61a94f6e 100644 --- a/MediaBrowser.Model/Services/IAsyncStreamWriter.cs +++ b/MediaBrowser.Model/Services/IAsyncStreamWriter.cs @@ -8,4 +8,9 @@ namespace MediaBrowser.Model.Services { Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken); } + + public interface IFileWriter + { + Task WriteToAsync(Stream responseStream, CancellationToken cancellationToken); + } } diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index e9a9f1c5b..40cef4ec0 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -2,6 +2,8 @@ using System.Collections.Generic; using System.IO; using System.Net; +using System.Threading; +using System.Threading.Tasks; namespace MediaBrowser.Model.Services { @@ -151,6 +153,7 @@ namespace MediaBrowser.Model.Services //Add Metadata to Response Dictionary Items { get; } - } + Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken); + } } diff --git a/SharedVersion.cs b/SharedVersion.cs index e3fd5e349..3f2c5d97e 100644 --- a/SharedVersion.cs +++ b/SharedVersion.cs @@ -1,3 +1,3 @@ using System.Reflection; -[assembly: AssemblyVersion("3.2.7.2")] +[assembly: AssemblyVersion("3.2.7.3")] diff --git a/SocketHttpListener.Portable/Net/EndPointListener.cs b/SocketHttpListener.Portable/Net/EndPointListener.cs index c7642d5d1..4f1a17fc0 100644 --- a/SocketHttpListener.Portable/Net/EndPointListener.cs +++ b/SocketHttpListener.Portable/Net/EndPointListener.cs @@ -32,8 +32,9 @@ namespace SocketHttpListener.Net private readonly ISocketFactory _socketFactory; private readonly ITextEncoding _textEncoding; private readonly IMemoryStreamFactory _memoryStreamFactory; + private readonly IFileSystem _fileSystem; - public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding) + public EndPointListener(HttpListener listener, IpAddressInfo addr, int port, bool secure, ICertificate cert, ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem) { this.listener = listener; _logger = logger; @@ -42,6 +43,7 @@ namespace SocketHttpListener.Net _socketFactory = socketFactory; _memoryStreamFactory = memoryStreamFactory; _textEncoding = textEncoding; + _fileSystem = fileSystem; this.secure = secure; this.cert = cert; @@ -107,7 +109,7 @@ namespace SocketHttpListener.Net return; } - HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding).ConfigureAwait(false); + HttpConnection conn = await HttpConnection.Create(_logger, accepted, listener, listener.secure, listener.cert, _cryptoProvider, _streamFactory, _memoryStreamFactory, _textEncoding, _fileSystem).ConfigureAwait(false); //_logger.Debug("Adding unregistered connection to {0}. Id: {1}", accepted.RemoteEndPoint, connectionId); lock (listener.unregistered) diff --git a/SocketHttpListener.Portable/Net/EndPointManager.cs b/SocketHttpListener.Portable/Net/EndPointManager.cs index 797684b3e..11f774915 100644 --- a/SocketHttpListener.Portable/Net/EndPointManager.cs +++ b/SocketHttpListener.Portable/Net/EndPointManager.cs @@ -5,6 +5,7 @@ using System.Linq; using System.Net; using System.Reflection; using System.Threading.Tasks; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Net; using SocketHttpListener.Primitives; @@ -105,7 +106,7 @@ namespace SocketHttpListener.Net } else { - epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding); + epl = new EndPointListener(listener, addr, port, secure, listener.Certificate, logger, listener.CryptoProvider, listener.StreamFactory, listener.SocketFactory, listener.MemoryStreamFactory, listener.TextEncoding, listener.FileSystem); p[port] = epl; } diff --git a/SocketHttpListener.Portable/Net/HttpConnection.cs b/SocketHttpListener.Portable/Net/HttpConnection.cs index 4a2cf3f5b..5fe47fc63 100644 --- a/SocketHttpListener.Portable/Net/HttpConnection.cs +++ b/SocketHttpListener.Portable/Net/HttpConnection.cs @@ -35,13 +35,14 @@ namespace SocketHttpListener.Net ICertificate cert; Stream ssl_stream; - private ILogger _logger; + private readonly ILogger _logger; private readonly ICryptoProvider _cryptoProvider; private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly ITextEncoding _textEncoding; private readonly IStreamFactory _streamFactory; + private readonly IFileSystem _fileSystem; - private HttpConnection(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding) + private HttpConnection(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem) { _logger = logger; this.sock = sock; @@ -51,6 +52,7 @@ namespace SocketHttpListener.Net _cryptoProvider = cryptoProvider; _memoryStreamFactory = memoryStreamFactory; _textEncoding = textEncoding; + _fileSystem = fileSystem; _streamFactory = streamFactory; } @@ -82,9 +84,9 @@ namespace SocketHttpListener.Net Init(); } - public static async Task Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding) + public static async Task Create(ILogger logger, IAcceptSocket sock, EndPointListener epl, bool secure, ICertificate cert, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem) { - var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding); + var connection = new HttpConnection(logger, sock, epl, secure, cert, cryptoProvider, streamFactory, memoryStreamFactory, textEncoding, fileSystem); await connection.InitStream().ConfigureAwait(false); @@ -121,7 +123,7 @@ namespace SocketHttpListener.Net position = 0; input_state = InputState.RequestLine; line_state = LineState.None; - context = new HttpListenerContext(this, _logger, _cryptoProvider, _memoryStreamFactory, _textEncoding); + context = new HttpListenerContext(this, _logger, _cryptoProvider, _memoryStreamFactory, _textEncoding, _fileSystem); } public bool IsClosed @@ -213,7 +215,9 @@ namespace SocketHttpListener.Net if (context.Response.SendChunked || isExpect100Continue || context.Request.IsWebSocketRequest || true) { - o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding); + var supportsDirectSocketAccess = !context.Response.SendChunked && !isExpect100Continue && !secure; + + o_stream = new ResponseStream(stream, context.Response, _memoryStreamFactory, _textEncoding, _fileSystem, sock, supportsDirectSocketAccess); } else { diff --git a/SocketHttpListener.Portable/Net/HttpListener.cs b/SocketHttpListener.Portable/Net/HttpListener.cs index 2b0f75d01..c2e7acd8e 100644 --- a/SocketHttpListener.Portable/Net/HttpListener.cs +++ b/SocketHttpListener.Portable/Net/HttpListener.cs @@ -18,6 +18,7 @@ namespace SocketHttpListener.Net internal ICryptoProvider CryptoProvider { get; private set; } internal IStreamFactory StreamFactory { get; private set; } internal ISocketFactory SocketFactory { get; private set; } + internal IFileSystem FileSystem { get; private set; } internal ITextEncoding TextEncoding { get; private set; } internal IMemoryStreamFactory MemoryStreamFactory { get; private set; } internal INetworkManager NetworkManager { get; private set; } @@ -39,7 +40,7 @@ namespace SocketHttpListener.Net public Action OnContext { get; set; } - public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory) + public HttpListener(ILogger logger, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem) { _logger = logger; CryptoProvider = cryptoProvider; @@ -48,19 +49,20 @@ namespace SocketHttpListener.Net NetworkManager = networkManager; TextEncoding = textEncoding; MemoryStreamFactory = memoryStreamFactory; + FileSystem = fileSystem; prefixes = new HttpListenerPrefixCollection(logger, this); registry = new Dictionary(); connections = new Dictionary(); auth_schemes = AuthenticationSchemes.Anonymous; } - public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory) - :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory) + public HttpListener(ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem) + :this(new NullLogger(), certificate, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem) { } - public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory) - : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory) + public HttpListener(ILogger logger, ICertificate certificate, ICryptoProvider cryptoProvider, IStreamFactory streamFactory, ISocketFactory socketFactory, INetworkManager networkManager, ITextEncoding textEncoding, IMemoryStreamFactory memoryStreamFactory, IFileSystem fileSystem) + : this(logger, cryptoProvider, streamFactory, socketFactory, networkManager, textEncoding, memoryStreamFactory, fileSystem) { _certificate = certificate; } diff --git a/SocketHttpListener.Portable/Net/HttpListenerContext.cs b/SocketHttpListener.Portable/Net/HttpListenerContext.cs index 182fd2d2a..58d769f22 100644 --- a/SocketHttpListener.Portable/Net/HttpListenerContext.cs +++ b/SocketHttpListener.Portable/Net/HttpListenerContext.cs @@ -18,20 +18,18 @@ namespace SocketHttpListener.Net HttpConnection cnc; string error; int err_status = 400; - private readonly ILogger _logger; private readonly ICryptoProvider _cryptoProvider; private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly ITextEncoding _textEncoding; - internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding) + internal HttpListenerContext(HttpConnection cnc, ILogger logger, ICryptoProvider cryptoProvider, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem) { this.cnc = cnc; - _logger = logger; _cryptoProvider = cryptoProvider; _memoryStreamFactory = memoryStreamFactory; _textEncoding = textEncoding; request = new HttpListenerRequest(this, _textEncoding); - response = new HttpListenerResponse(this, _logger, _textEncoding); + response = new HttpListenerResponse(this, logger, _textEncoding, fileSystem); } internal int ErrorStatus diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs index 9a5862cb9..d8011f05e 100644 --- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs +++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs @@ -3,6 +3,9 @@ using System.Globalization; using System.IO; using System.Net; using System.Text; +using System.Threading; +using System.Threading.Tasks; +using MediaBrowser.Model.IO; using MediaBrowser.Model.Logging; using MediaBrowser.Model.Text; using SocketHttpListener.Primitives; @@ -32,12 +35,14 @@ namespace SocketHttpListener.Net private readonly ILogger _logger; private readonly ITextEncoding _textEncoding; + private readonly IFileSystem _fileSystem; - internal HttpListenerResponse(HttpListenerContext context, ILogger logger, ITextEncoding textEncoding) + internal HttpListenerResponse(HttpListenerContext context, ILogger logger, ITextEncoding textEncoding, IFileSystem fileSystem) { this.context = context; _logger = logger; _textEncoding = textEncoding; + _fileSystem = fileSystem; } internal bool CloseConnection @@ -366,7 +371,7 @@ namespace SocketHttpListener.Net { if (chunked) { - return ; + return; } Version v = context.Request.ProtocolVersion; @@ -509,5 +514,10 @@ namespace SocketHttpListener.Net cookies.Add(cookie); } + + public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + { + return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, cancellationToken); + } } } \ No newline at end of file diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs index a79a18791..ccc0efc55 100644 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ b/SocketHttpListener.Portable/Net/ResponseStream.cs @@ -5,6 +5,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.IO; +using MediaBrowser.Model.Net; using MediaBrowser.Model.Text; using SocketHttpListener.Primitives; @@ -22,12 +23,18 @@ namespace SocketHttpListener.Net Stream stream; private readonly IMemoryStreamFactory _memoryStreamFactory; private readonly ITextEncoding _textEncoding; + private readonly IFileSystem _fileSystem; + private readonly IAcceptSocket _socket; + private readonly bool _supportsDirectSocketAccess; - internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding) + internal ResponseStream(Stream stream, HttpListenerResponse response, IMemoryStreamFactory memoryStreamFactory, ITextEncoding textEncoding, IFileSystem fileSystem, IAcceptSocket socket, bool supportsDirectSocketAccess) { this.response = response; _memoryStreamFactory = memoryStreamFactory; _textEncoding = textEncoding; + _fileSystem = fileSystem; + _socket = socket; + _supportsDirectSocketAccess = supportsDirectSocketAccess; this.stream = stream; } @@ -299,5 +306,80 @@ namespace SocketHttpListener.Net { throw new NotSupportedException(); } + + public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + { + //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked) + //{ + // return TransmitFileOverSocket(path, offset, count, cancellationToken); + //} + return TransmitFileManaged(path, offset, count, cancellationToken); + } + + private readonly byte[] _emptyBuffer = new byte[] { }; + private async Task TransmitFileOverSocket(string path, long offset, long count, CancellationToken cancellationToken) + { + MemoryStream ms = GetHeaders(response, _memoryStreamFactory, false); + + var buffer = new byte[] {}; + if (ms != null) + { + ms.Position = 0; + + byte[] msBuffer; + _memoryStreamFactory.TryGetBuffer(ms, out msBuffer); + buffer = msBuffer; + } + + await _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken).ConfigureAwait(false); + } + + private async Task TransmitFileManaged(string path, long offset, long count, CancellationToken cancellationToken) + { + var chunked = response.SendChunked; + + if (!chunked) + { + await WriteAsync(_emptyBuffer, 0, 0, cancellationToken).ConfigureAwait(false); + } + + using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) + { + if (offset > 0) + { + fs.Position = offset; + } + + var targetStream = chunked ? this : stream; + + if (count > 0) + { + await CopyToInternalAsync(fs, targetStream, count, cancellationToken).ConfigureAwait(false); + } + else + { + await fs.CopyToAsync(targetStream, 81920, cancellationToken).ConfigureAwait(false); + } + } + } + + private async Task CopyToInternalAsync(Stream source, Stream destination, long copyLength, CancellationToken cancellationToken) + { + var array = new byte[81920]; + int count; + while ((count = await source.ReadAsync(array, 0, array.Length, cancellationToken).ConfigureAwait(false)) != 0) + { + var bytesToCopy = Math.Min(count, copyLength); + + await destination.WriteAsync(array, 0, Convert.ToInt32(bytesToCopy), cancellationToken).ConfigureAwait(false); + + copyLength -= bytesToCopy; + + if (copyLength <= 0) + { + break; + } + } + } } } -- cgit v1.2.3 From b38b7a706268fe5d92d8cbe703a188b58ed7ec4d Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 00:08:23 -0400 Subject: rework filestream --- Emby.Common.Implementations/Net/NetAcceptSocket.cs | 2 +- Emby.Common.Implementations/Net/SocketFactory.cs | 27 +++++++++++++++++++--- .../HttpServer/FileWriter.cs | 7 ++++-- .../HttpServer/HttpResultFactory.cs | 5 ++-- .../SocketSharp/WebSocketSharpResponse.cs | 4 ++-- .../Library/LibraryManager.cs | 27 ++++++++++++++-------- MediaBrowser.Api/StartupWizardService.cs | 1 + .../Entities/Audio/MusicArtist.cs | 7 +++++- .../Entities/Audio/MusicGenre.cs | 7 +++++- MediaBrowser.Controller/Entities/GameGenre.cs | 7 +++++- MediaBrowser.Controller/Entities/Genre.cs | 7 +++++- MediaBrowser.Controller/Entities/Person.cs | 7 +++++- MediaBrowser.Controller/Entities/Studio.cs | 7 +++++- MediaBrowser.Controller/Entities/Year.cs | 7 +++++- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 ++ .../Configuration/ServerConfiguration.cs | 1 + MediaBrowser.Model/Net/ISocketFactory.cs | 2 ++ MediaBrowser.Model/Services/IRequest.cs | 3 ++- .../Net/HttpListenerResponse.cs | 4 ++-- SocketHttpListener.Portable/Net/ResponseStream.cs | 8 +++---- 20 files changed, 108 insertions(+), 34 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/NetAcceptSocket.cs b/Emby.Common.Implementations/Net/NetAcceptSocket.cs index e21ffe553..3721709e6 100644 --- a/Emby.Common.Implementations/Net/NetAcceptSocket.cs +++ b/Emby.Common.Implementations/Net/NetAcceptSocket.cs @@ -100,7 +100,7 @@ namespace Emby.Common.Implementations.Net #if NET46 public Task SendFile(string path, byte[] preBuffer, byte[] postBuffer, CancellationToken cancellationToken) { - var options = TransmitFileOptions.Disconnect | TransmitFileOptions.ReuseSocket | TransmitFileOptions.UseKernelApc; + var options = TransmitFileOptions.UseKernelApc; var completionSource = new TaskCompletionSource(); diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index 021613e57..0f4306a6b 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -97,10 +97,31 @@ namespace Emby.Common.Implementations.Net } } + public ISocket CreateUdpBroadcastSocket(int localPort) + { + if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); + + var retVal = new Socket(AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Dgram, System.Net.Sockets.ProtocolType.Udp); + try + { + retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true); + retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1); + + return new UdpSocket(retVal, localPort, IPAddress.Any); + } + catch + { + if (retVal != null) + retVal.Dispose(); + + throw; + } + } + /// - /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port. - /// - /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. + /// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port. + /// + /// An implementation of the interface used by RSSDP components to perform acceptSocket operations. public ISocket CreateSsdpUdpSocket(IpAddressInfo localIpAddress, int localPort) { if (localPort < 0) throw new ArgumentException("localPort cannot be less than zero.", "localPort"); diff --git a/Emby.Server.Implementations/HttpServer/FileWriter.cs b/Emby.Server.Implementations/HttpServer/FileWriter.cs index b80a40962..d230a9b91 100644 --- a/Emby.Server.Implementations/HttpServer/FileWriter.cs +++ b/Emby.Server.Implementations/HttpServer/FileWriter.cs @@ -27,6 +27,8 @@ namespace Emby.Server.Implementations.HttpServer private static readonly CultureInfo UsCulture = new CultureInfo("en-US"); public List Cookies { get; private set; } + public FileShareMode FileShare { get; set; } + /// /// The _options /// @@ -69,6 +71,7 @@ namespace Emby.Server.Implementations.HttpServer SetRangeValues(); } + FileShare = FileShareMode.Read; Cookies = new List(); } @@ -153,11 +156,11 @@ namespace Emby.Server.Implementations.HttpServer if (string.IsNullOrWhiteSpace(RangeHeader) || (RangeStart <= 0 && RangeEnd >= TotalContentLength - 1)) { Logger.Info("Transmit file {0}", Path); - await response.TransmitFile(Path, 0, 0, cancellationToken).ConfigureAwait(false); + await response.TransmitFile(Path, 0, 0, FileShare, cancellationToken).ConfigureAwait(false); return; } - await response.TransmitFile(Path, RangeStart, RangeEnd, cancellationToken).ConfigureAwait(false); + await response.TransmitFile(Path, RangeStart, RangeEnd, FileShare, cancellationToken).ConfigureAwait(false); } finally { diff --git a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs index e3f105941..310161d41 100644 --- a/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs +++ b/Emby.Server.Implementations/HttpServer/HttpResultFactory.cs @@ -556,12 +556,13 @@ namespace Emby.Server.Implementations.HttpServer { var rangeHeader = requestContext.Headers.Get("Range"); - if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path) && options.FileShare == FileShareMode.Read) + if (!isHeadRequest && !string.IsNullOrWhiteSpace(options.Path)) { return new FileWriter(options.Path, contentType, rangeHeader, _logger, _fileSystem) { OnComplete = options.OnComplete, - OnError = options.OnError + OnError = options.OnError, + FileShare = options.FileShare }; } diff --git a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs index a497ee715..fd30b227f 100644 --- a/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs +++ b/Emby.Server.Implementations/HttpServer/SocketSharp/WebSocketSharpResponse.cs @@ -193,9 +193,9 @@ namespace Emby.Server.Implementations.HttpServer.SocketSharp { } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - return _response.TransmitFile(path, offset, count, cancellationToken); + return _response.TransmitFile(path, offset, count, fileShareMode, cancellationToken); } } } diff --git a/Emby.Server.Implementations/Library/LibraryManager.cs b/Emby.Server.Implementations/Library/LibraryManager.cs index f7706db47..026486efc 100644 --- a/Emby.Server.Implementations/Library/LibraryManager.cs +++ b/Emby.Server.Implementations/Library/LibraryManager.cs @@ -513,6 +513,11 @@ namespace Emby.Server.Implementations.Library } public Guid GetNewItemId(string key, Type type) + { + return GetNewItemIdInternal(key, type, false); + } + + private Guid GetNewItemIdInternal(string key, Type type, bool forceCaseInsensitive) { if (string.IsNullOrWhiteSpace(key)) { @@ -531,7 +536,7 @@ namespace Emby.Server.Implementations.Library .Replace("/", "\\"); } - if (!ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) + if (forceCaseInsensitive || !ConfigurationManager.Configuration.EnableCaseSensitiveItemIds) { key = key.ToLower(); } @@ -865,7 +870,7 @@ namespace Emby.Server.Implementations.Library /// Task{Person}. public Person GetPerson(string name) { - return CreateItemByName(Person.GetPath(name), name); + return CreateItemByName(Person.GetPath, name); } /// @@ -875,7 +880,7 @@ namespace Emby.Server.Implementations.Library /// Task{Studio}. public Studio GetStudio(string name) { - return CreateItemByName(Studio.GetPath(name), name); + return CreateItemByName(Studio.GetPath, name); } /// @@ -885,7 +890,7 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public Genre GetGenre(string name) { - return CreateItemByName(Genre.GetPath(name), name); + return CreateItemByName(Genre.GetPath, name); } /// @@ -895,7 +900,7 @@ namespace Emby.Server.Implementations.Library /// Task{MusicGenre}. public MusicGenre GetMusicGenre(string name) { - return CreateItemByName(MusicGenre.GetPath(name), name); + return CreateItemByName(MusicGenre.GetPath, name); } /// @@ -905,7 +910,7 @@ namespace Emby.Server.Implementations.Library /// Task{GameGenre}. public GameGenre GetGameGenre(string name) { - return CreateItemByName(GameGenre.GetPath(name), name); + return CreateItemByName(GameGenre.GetPath, name); } /// @@ -923,7 +928,7 @@ namespace Emby.Server.Implementations.Library var name = value.ToString(CultureInfo.InvariantCulture); - return CreateItemByName(Year.GetPath(name), name); + return CreateItemByName(Year.GetPath, name); } /// @@ -933,10 +938,10 @@ namespace Emby.Server.Implementations.Library /// Task{Genre}. public MusicArtist GetArtist(string name) { - return CreateItemByName(MusicArtist.GetPath(name), name); + return CreateItemByName(MusicArtist.GetPath, name); } - private T CreateItemByName(string path, string name) + private T CreateItemByName(Func getPathFn, string name) where T : BaseItem, new() { if (typeof(T) == typeof(MusicArtist)) @@ -957,7 +962,9 @@ namespace Emby.Server.Implementations.Library } } - var id = GetNewItemId(path, typeof(T)); + var path = getPathFn(name); + var forceCaseInsensitiveId = ConfigurationManager.Configuration.EnableNormalizedItemByNameIds; + var id = GetNewItemIdInternal(path, typeof(T), forceCaseInsensitiveId); var item = GetItemById(id) as T; diff --git a/MediaBrowser.Api/StartupWizardService.cs b/MediaBrowser.Api/StartupWizardService.cs index e010f122c..02154b98d 100644 --- a/MediaBrowser.Api/StartupWizardService.cs +++ b/MediaBrowser.Api/StartupWizardService.cs @@ -120,6 +120,7 @@ namespace MediaBrowser.Api config.EnableSeriesPresentationUniqueKey = true; config.EnableLocalizedGuids = true; config.EnableSimpleArtistDetection = true; + config.EnableNormalizedItemByNameIds = true; } public void Post(UpdateStartupConfiguration request) diff --git a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs index 20b2529c0..8d83f8a35 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicArtist.cs @@ -289,7 +289,12 @@ namespace MediaBrowser.Controller.Entities.Audio } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs index 74679b474..e26e0dfce 100644 --- a/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs +++ b/MediaBrowser.Controller/Entities/Audio/MusicGenre.cs @@ -118,7 +118,12 @@ namespace MediaBrowser.Controller.Entities.Audio return LibraryManager.GetItemList(query); } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/GameGenre.cs b/MediaBrowser.Controller/Entities/GameGenre.cs index 22a8675c5..4187167b9 100644 --- a/MediaBrowser.Controller/Entities/GameGenre.cs +++ b/MediaBrowser.Controller/Entities/GameGenre.cs @@ -96,7 +96,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Genre.cs b/MediaBrowser.Controller/Entities/Genre.cs index 1b746ae51..9769efdd0 100644 --- a/MediaBrowser.Controller/Entities/Genre.cs +++ b/MediaBrowser.Controller/Entities/Genre.cs @@ -108,7 +108,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Person.cs b/MediaBrowser.Controller/Entities/Person.cs index ee1aea938..b68681d36 100644 --- a/MediaBrowser.Controller/Entities/Person.cs +++ b/MediaBrowser.Controller/Entities/Person.cs @@ -133,7 +133,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validFilename = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Studio.cs b/MediaBrowser.Controller/Entities/Studio.cs index b8ad691a9..2e5e6ce43 100644 --- a/MediaBrowser.Controller/Entities/Studio.cs +++ b/MediaBrowser.Controller/Entities/Studio.cs @@ -114,7 +114,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/Entities/Year.cs b/MediaBrowser.Controller/Entities/Year.cs index 75fb69435..b352950a0 100644 --- a/MediaBrowser.Controller/Entities/Year.cs +++ b/MediaBrowser.Controller/Entities/Year.cs @@ -122,7 +122,12 @@ namespace MediaBrowser.Controller.Entities } } - public static string GetPath(string name, bool normalizeName = true) + public static string GetPath(string name) + { + return GetPath(name, true); + } + + public static string GetPath(string name, bool normalizeName) { // Trim the period at the end because windows will have a hard time with that var validName = normalizeName ? diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index 5615649c2..af1c0d12e 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -44,6 +44,8 @@ namespace MediaBrowser.Controller.LiveTv /// The cancellation token. /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); + + Task> DiscoverDevices(int discoveryDurationMs); } public interface IConfigurableTunerHost { diff --git a/MediaBrowser.Model/Configuration/ServerConfiguration.cs b/MediaBrowser.Model/Configuration/ServerConfiguration.cs index c2b1e3c89..0562d0ac5 100644 --- a/MediaBrowser.Model/Configuration/ServerConfiguration.cs +++ b/MediaBrowser.Model/Configuration/ServerConfiguration.cs @@ -48,6 +48,7 @@ namespace MediaBrowser.Model.Configuration public bool EnableHttps { get; set; } public bool EnableSeriesPresentationUniqueKey { get; set; } public bool EnableLocalizedGuids { get; set; } + public bool EnableNormalizedItemByNameIds { get; set; } /// /// Gets or sets the value pointing to the file system where the ssl certiifcate is located.. diff --git a/MediaBrowser.Model/Net/ISocketFactory.cs b/MediaBrowser.Model/Net/ISocketFactory.cs index 4b70f3362..e7dbf6cb1 100644 --- a/MediaBrowser.Model/Net/ISocketFactory.cs +++ b/MediaBrowser.Model/Net/ISocketFactory.cs @@ -14,6 +14,8 @@ namespace MediaBrowser.Model.Net /// A implementation. ISocket CreateUdpSocket(int localPort); + ISocket CreateUdpBroadcastSocket(int localPort); + ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort); /// diff --git a/MediaBrowser.Model/Services/IRequest.cs b/MediaBrowser.Model/Services/IRequest.cs index 40cef4ec0..115ba25ce 100644 --- a/MediaBrowser.Model/Services/IRequest.cs +++ b/MediaBrowser.Model/Services/IRequest.cs @@ -4,6 +4,7 @@ using System.IO; using System.Net; using System.Threading; using System.Threading.Tasks; +using MediaBrowser.Model.IO; namespace MediaBrowser.Model.Services { @@ -154,6 +155,6 @@ namespace MediaBrowser.Model.Services //Add Metadata to Response Dictionary Items { get; } - Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken); + Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken); } } diff --git a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs index d8011f05e..d9f91c0cc 100644 --- a/SocketHttpListener.Portable/Net/HttpListenerResponse.cs +++ b/SocketHttpListener.Portable/Net/HttpListenerResponse.cs @@ -515,9 +515,9 @@ namespace SocketHttpListener.Net cookies.Add(cookie); } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { - return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, cancellationToken); + return ((ResponseStream)OutputStream).TransmitFile(path, offset, count, fileShareMode, cancellationToken); } } } \ No newline at end of file diff --git a/SocketHttpListener.Portable/Net/ResponseStream.cs b/SocketHttpListener.Portable/Net/ResponseStream.cs index ccc0efc55..19821f954 100644 --- a/SocketHttpListener.Portable/Net/ResponseStream.cs +++ b/SocketHttpListener.Portable/Net/ResponseStream.cs @@ -307,13 +307,13 @@ namespace SocketHttpListener.Net throw new NotSupportedException(); } - public Task TransmitFile(string path, long offset, long count, CancellationToken cancellationToken) + public Task TransmitFile(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { //if (_supportsDirectSocketAccess && offset == 0 && count == 0 && !response.SendChunked) //{ // return TransmitFileOverSocket(path, offset, count, cancellationToken); //} - return TransmitFileManaged(path, offset, count, cancellationToken); + return TransmitFileManaged(path, offset, count, fileShareMode, cancellationToken); } private readonly byte[] _emptyBuffer = new byte[] { }; @@ -334,7 +334,7 @@ namespace SocketHttpListener.Net await _socket.SendFile(path, buffer, _emptyBuffer, cancellationToken).ConfigureAwait(false); } - private async Task TransmitFileManaged(string path, long offset, long count, CancellationToken cancellationToken) + private async Task TransmitFileManaged(string path, long offset, long count, FileShareMode fileShareMode, CancellationToken cancellationToken) { var chunked = response.SendChunked; @@ -343,7 +343,7 @@ namespace SocketHttpListener.Net await WriteAsync(_emptyBuffer, 0, 0, cancellationToken).ConfigureAwait(false); } - using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, FileShareMode.Read, true)) + using (var fs = _fileSystem.GetFileStream(path, FileOpenMode.Open, FileAccessMode.Read, fileShareMode, true)) { if (offset > 0) { -- cgit v1.2.3 From f05dc08c06a386aca052a2abb981e309bd005d91 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Mon, 13 Mar 2017 00:49:10 -0400 Subject: update tuner discovery --- Emby.Common.Implementations/Net/UdpSocket.cs | 18 ++++---- .../LiveTv/EmbyTV/EmbyTV.cs | 54 ++++++++++++++++++++++ .../LiveTv/LiveTvManager.cs | 2 + .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 4 +- .../LiveTv/TunerHosts/M3UTunerHost.cs | 2 +- MediaBrowser.Controller/LiveTv/ITunerHost.cs | 2 +- 6 files changed, 70 insertions(+), 12 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 8e2a1da6f..b85245ba1 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -60,6 +60,8 @@ namespace Emby.Common.Implementations.Net var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; + cancellationToken.Register(() => tcs.TrySetCanceled()); + #if NETSTANDARD1_6 _Socket.ReceiveFromAsync(new ArraySegment(state.Buffer), SocketFlags.None, state.RemoteEndPoint) .ContinueWith((task, asyncState) => @@ -160,7 +162,7 @@ namespace Emby.Common.Implementations.Net var bytesRead = receiveData(); var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.SetResult( + state.TaskCompletionSource.TrySetResult( new SocketReceiveResult { Buffer = state.Buffer, @@ -172,18 +174,18 @@ namespace Emby.Common.Implementations.Net } catch (ObjectDisposedException) { - state.TaskCompletionSource.SetCanceled(); + state.TaskCompletionSource.TrySetCanceled(); } catch (SocketException se) { if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown) - state.TaskCompletionSource.SetException(se); + state.TaskCompletionSource.TrySetException(se); else - state.TaskCompletionSource.SetCanceled(); + state.TaskCompletionSource.TrySetCanceled(); } catch (Exception ex) { - state.TaskCompletionSource.SetException(ex); + state.TaskCompletionSource.TrySetException(ex); } } @@ -206,7 +208,7 @@ namespace Emby.Common.Implementations.Net var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint); var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.SetResult( + state.TaskCompletionSource.TrySetResult( new SocketReceiveResult { Buffer = state.Buffer, @@ -218,11 +220,11 @@ namespace Emby.Common.Implementations.Net } catch (ObjectDisposedException) { - state.TaskCompletionSource.SetCanceled(); + state.TaskCompletionSource.TrySetCanceled(); } catch (Exception ex) { - state.TaskCompletionSource.SetException(ex); + state.TaskCompletionSource.TrySetException(ex); } #endif } diff --git a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs index d9060a066..5c7c28cc8 100644 --- a/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs +++ b/Emby.Server.Implementations/LiveTv/EmbyTV/EmbyTV.cs @@ -2542,6 +2542,60 @@ namespace Emby.Server.Implementations.LiveTv.EmbyTV public ProgramInfo Program { get; set; } public CancellationTokenSource CancellationTokenSource { get; set; } } + + public async Task ScanForTunerDeviceChanges(CancellationToken cancellationToken) + { + foreach (var host in _liveTvManager.TunerHosts) + { + await ScanForTunerDeviceChanges(host, cancellationToken).ConfigureAwait(false); + } + } + + private async Task ScanForTunerDeviceChanges(ITunerHost host, CancellationToken cancellationToken) + { + var discoveredDevices = await DiscoverDevices(host, 2000, cancellationToken).ConfigureAwait(false); + + var configuredDevices = GetConfiguration().TunerHosts + .Where(i => string.Equals(i.Type, host.Type, StringComparison.OrdinalIgnoreCase)) + .ToList(); + + foreach (var device in discoveredDevices) + { + var configuredDevice = configuredDevices.FirstOrDefault(i => string.Equals(i.DeviceId, device.DeviceId, StringComparison.OrdinalIgnoreCase)); + + if (configuredDevice != null) + { + if (!string.Equals(device.Url, configuredDevice.Url, StringComparison.OrdinalIgnoreCase)) + { + _logger.Info("Tuner url has changed from {0} to {1}", configuredDevice.Url, device.Url); + + configuredDevice.Url = device.Url; + await _liveTvManager.SaveTunerHost(configuredDevice).ConfigureAwait(false); + } + } + } + } + + private async Task> DiscoverDevices(ITunerHost host, int discoveryDuationMs, CancellationToken cancellationToken) + { + try + { + var discoveredDevices = await host.DiscoverDevices(discoveryDuationMs, cancellationToken).ConfigureAwait(false); + + foreach (var device in discoveredDevices) + { + _logger.Info("Discovered tuner device {0} at {1}", host.Name, device.Url); + } + + return discoveredDevices; + } + catch (Exception ex) + { + _logger.ErrorException("Error discovering tuner devices", ex); + + return new List(); + } + } } public static class ConfigurationExtension { diff --git a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs index 618cd1d45..fc2e0ce72 100644 --- a/Emby.Server.Implementations/LiveTv/LiveTvManager.cs +++ b/Emby.Server.Implementations/LiveTv/LiveTvManager.cs @@ -1180,6 +1180,8 @@ namespace Emby.Server.Implementations.LiveTv { EmbyTV.EmbyTV.Current.CreateRecordingFolders(); + await EmbyTV.EmbyTV.Current.ScanForTunerDeviceChanges(cancellationToken).ConfigureAwait(false); + var numComplete = 0; double progressPerService = _services.Count == 0 ? 0 diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 487aa7ca9..6b80cec19 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -653,9 +653,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public int TunerCount { get; set; } } - public async Task> DiscoverDevices(int discoveryDurationMs) + public async Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken) { - var cancellationToken = new CancellationTokenSource(discoveryDurationMs).Token; + cancellationToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(discoveryDurationMs).Token, cancellationToken).Token; var list = new List(); // Create udp broadcast discovery message diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs index a09da3e53..cc0ae0983 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/M3UTunerHost.cs @@ -177,7 +177,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts return Task.FromResult(true); } - public Task> DiscoverDevices(int discoveryDurationMs) + public Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken) { return Task.FromResult(new List()); } diff --git a/MediaBrowser.Controller/LiveTv/ITunerHost.cs b/MediaBrowser.Controller/LiveTv/ITunerHost.cs index af1c0d12e..fc344298b 100644 --- a/MediaBrowser.Controller/LiveTv/ITunerHost.cs +++ b/MediaBrowser.Controller/LiveTv/ITunerHost.cs @@ -45,7 +45,7 @@ namespace MediaBrowser.Controller.LiveTv /// Task<List<MediaSourceInfo>>. Task> GetChannelStreamMediaSources(string channelId, CancellationToken cancellationToken); - Task> DiscoverDevices(int discoveryDurationMs); + Task> DiscoverDevices(int discoveryDurationMs, CancellationToken cancellationToken); } public interface IConfigurableTunerHost { -- cgit v1.2.3 From c9ee7633caacb548fd54c33c7c9df1be29b71d29 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Fri, 17 Mar 2017 16:23:34 -0400 Subject: embed recording button into video player --- Emby.Common.Implementations/Net/SocketFactory.cs | 12 ++++++++++++ MediaBrowser.Api/Playback/Hls/BaseHlsService.cs | 7 +++++-- MediaBrowser.Api/Playback/StreamRequest.cs | 3 +++ MediaBrowser.Api/Playback/StreamState.cs | 18 ++++++++++++++++++ MediaBrowser.Model/Dlna/StreamBuilder.cs | 9 +++++++++ MediaBrowser.Model/Dlna/StreamInfo.cs | 14 ++++++++++++++ MediaBrowser.Model/Dlna/TranscodingProfile.cs | 6 ++++++ 7 files changed, 67 insertions(+), 2 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/SocketFactory.cs b/Emby.Common.Implementations/Net/SocketFactory.cs index 0f4306a6b..1fd367afb 100644 --- a/Emby.Common.Implementations/Net/SocketFactory.cs +++ b/Emby.Common.Implementations/Net/SocketFactory.cs @@ -52,6 +52,18 @@ namespace Emby.Common.Implementations.Net { throw new SocketCreateException(ex.SocketErrorCode.ToString(), ex); } + catch (ArgumentException ex) + { + if (dualMode) + { + // Mono for BSD incorrectly throws ArgumentException instead of SocketException + throw new SocketCreateException("AddressFamilyNotSupported", ex); + } + else + { + throw; + } + } } public ISocket CreateTcpSocket(IpAddressInfo remoteAddress, int remotePort) diff --git a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs index 98115b840..d2f14f4b8 100644 --- a/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs +++ b/MediaBrowser.Api/Playback/Hls/BaseHlsService.cs @@ -110,8 +110,11 @@ namespace MediaBrowser.Api.Playback.Hls throw; } - var waitForSegments = state.SegmentLength >= 10 ? 2 : 3; - await WaitForMinimumSegmentCount(playlist, waitForSegments, cancellationTokenSource.Token).ConfigureAwait(false); + var minSegments = state.MinSegments; + if (minSegments > 0) + { + await WaitForMinimumSegmentCount(playlist, minSegments, cancellationTokenSource.Token).ConfigureAwait(false); + } } } finally diff --git a/MediaBrowser.Api/Playback/StreamRequest.cs b/MediaBrowser.Api/Playback/StreamRequest.cs index d1bf9c120..f49fd87e0 100644 --- a/MediaBrowser.Api/Playback/StreamRequest.cs +++ b/MediaBrowser.Api/Playback/StreamRequest.cs @@ -42,6 +42,9 @@ namespace MediaBrowser.Api.Playback public string LiveStreamId { get; set; } public string Tag { get; set; } public string SegmentContainer { get; set; } + + public int? SegmentLength { get; set; } + public int? MinSegments { get; set; } } public class VideoStreamRequest : StreamRequest diff --git a/MediaBrowser.Api/Playback/StreamState.cs b/MediaBrowser.Api/Playback/StreamState.cs index 912d60889..22a0fa7c9 100644 --- a/MediaBrowser.Api/Playback/StreamState.cs +++ b/MediaBrowser.Api/Playback/StreamState.cs @@ -60,6 +60,11 @@ namespace MediaBrowser.Api.Playback { get { + if (Request.SegmentLength.HasValue) + { + return Request.SegmentLength.Value; + } + if (string.Equals(OutputVideoCodec, "copy", StringComparison.OrdinalIgnoreCase)) { var userAgent = UserAgent ?? string.Empty; @@ -86,6 +91,19 @@ namespace MediaBrowser.Api.Playback } } + public int MinSegments + { + get + { + if (Request.MinSegments.HasValue) + { + return Request.MinSegments.Value; + } + + return SegmentLength >= 10 ? 2 : 3; + } + } + public bool IsSegmentedLiveStream { get diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index 80d3ea3fb..bfb65ed42 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -484,6 +484,15 @@ namespace MediaBrowser.Model.Dlna playlistItem.CopyTimestamps = transcodingProfile.CopyTimestamps; playlistItem.EnableSubtitlesInManifest = transcodingProfile.EnableSubtitlesInManifest; + if (transcodingProfile.MinSegments > 0) + { + playlistItem.MinSegments = transcodingProfile.MinSegments; + } + if (transcodingProfile.SegmentLength > 0) + { + playlistItem.SegmentLength = transcodingProfile.SegmentLength; + } + if (!string.IsNullOrEmpty(transcodingProfile.MaxAudioChannels)) { int transcodingMaxAudioChannels; diff --git a/MediaBrowser.Model/Dlna/StreamInfo.cs b/MediaBrowser.Model/Dlna/StreamInfo.cs index 7e42e7fae..5705e6477 100644 --- a/MediaBrowser.Model/Dlna/StreamInfo.cs +++ b/MediaBrowser.Model/Dlna/StreamInfo.cs @@ -6,6 +6,7 @@ using MediaBrowser.Model.MediaInfo; using MediaBrowser.Model.Session; using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; namespace MediaBrowser.Model.Dlna @@ -37,6 +38,9 @@ namespace MediaBrowser.Model.Dlna public string VideoProfile { get; set; } + public int? SegmentLength { get; set; } + public int? MinSegments { get; set; } + public bool RequireAvc { get; set; } public bool DeInterlace { get; set; } public bool RequireNonAnamorphic { get; set; } @@ -291,6 +295,16 @@ namespace MediaBrowser.Model.Dlna if (!isDlna && isHls) { list.Add(new NameValuePair("SegmentContainer", item.Container ?? string.Empty)); + + if (item.SegmentLength.HasValue) + { + list.Add(new NameValuePair("SegmentLength", item.SegmentLength.Value.ToString(CultureInfo.InvariantCulture))); + } + + if (item.MinSegments.HasValue) + { + list.Add(new NameValuePair("MinSegments", item.MinSegments.Value.ToString(CultureInfo.InvariantCulture))); + } } return list; diff --git a/MediaBrowser.Model/Dlna/TranscodingProfile.cs b/MediaBrowser.Model/Dlna/TranscodingProfile.cs index 76f2332f2..350556e90 100644 --- a/MediaBrowser.Model/Dlna/TranscodingProfile.cs +++ b/MediaBrowser.Model/Dlna/TranscodingProfile.cs @@ -42,6 +42,12 @@ namespace MediaBrowser.Model.Dlna [XmlAttribute("maxAudioChannels")] public string MaxAudioChannels { get; set; } + [XmlAttribute("minSegments")] + public int MinSegments { get; set; } + + [XmlAttribute("segmentLength")] + public int SegmentLength { get; set; } + public List GetAudioCodecs() { List list = new List(); -- cgit v1.2.3 From 79f3eb3fe2b33a49ce43fe0c616671483e97efc3 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sat, 18 Mar 2017 19:37:29 -0400 Subject: update live tv osd --- Emby.Common.Implementations/IO/ManagedFileSystem.cs | 6 +++++- MediaBrowser.Model/Dlna/StreamBuilder.cs | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/IO/ManagedFileSystem.cs b/Emby.Common.Implementations/IO/ManagedFileSystem.cs index 2c9388a41..0c1c02cd5 100644 --- a/Emby.Common.Implementations/IO/ManagedFileSystem.cs +++ b/Emby.Common.Implementations/IO/ManagedFileSystem.cs @@ -28,7 +28,11 @@ namespace Emby.Common.Implementations.IO Logger = logger; _supportsAsyncFileStreams = true; _tempPath = tempPath; - EnableFileSystemRequestConcat = false; + + // On Linux, this needs to be true or symbolic links are ignored + EnableFileSystemRequestConcat = environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.Windows && + environmentInfo.OperatingSystem != MediaBrowser.Model.System.OperatingSystem.OSX; + SetInvalidFileNameChars(environmentInfo.OperatingSystem == MediaBrowser.Model.System.OperatingSystem.Windows); } diff --git a/MediaBrowser.Model/Dlna/StreamBuilder.cs b/MediaBrowser.Model/Dlna/StreamBuilder.cs index bfb65ed42..1c7aa81e5 100644 --- a/MediaBrowser.Model/Dlna/StreamBuilder.cs +++ b/MediaBrowser.Model/Dlna/StreamBuilder.cs @@ -1037,6 +1037,12 @@ namespace MediaBrowser.Model.Dlna private bool IsAudioEligibleForDirectPlay(MediaSourceInfo item, long? maxBitrate) { + // Don't restrict by bitrate if coming from an external domain + if (item.IsRemote) + { + return true; + } + if (!maxBitrate.HasValue) { _logger.Info("Cannot direct play due to unknown supported bitrate"); -- cgit v1.2.3 From 55bfb71baa3aa683b092c0432cbb5c7380a28395 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 12:26:52 -0400 Subject: update hdhomerun udp stream --- Emby.Common.Implementations/Net/UdpSocket.cs | 55 +++++++-- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 120 +++---------------- .../TunerHosts/HdHomerun/HdHomerunUdpStream.cs | 129 +-------------------- .../LiveTv/TunerHosts/MulticastStream.cs | 48 +++++++- .../LiveTv/TunerHosts/QueueStream.cs | 20 ++-- 5 files changed, 122 insertions(+), 250 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index b85245ba1..45cb42ecd 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -16,12 +16,15 @@ namespace Emby.Common.Implementations.Net internal sealed class UdpSocket : DisposableManagedObjectBase, ISocket { - - #region Fields - private Socket _Socket; private int _LocalPort; - #endregion + + private SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() + { + SocketFlags = SocketFlags.None + }; + + private TaskCompletionSource _currentReceiveTaskCompletionSource; public UdpSocket(Socket socket, int localPort, IPAddress ip) { @@ -32,6 +35,32 @@ namespace Emby.Common.Implementations.Net LocalIPAddress = NetworkManager.ToIpAddressInfo(ip); _Socket.Bind(new IPEndPoint(ip, _LocalPort)); + + InitReceiveSocketAsyncEventArgs(); + } + + private void InitReceiveSocketAsyncEventArgs() + { + var buffer = new byte[8192]; + _receiveSocketAsyncEventArgs.SetBuffer(buffer, 0, buffer.Length); + _receiveSocketAsyncEventArgs.Completed += _receiveSocketAsyncEventArgs_Completed; + } + + private void _receiveSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) + { + var tcs = _currentReceiveTaskCompletionSource; + if (tcs != null) + { + _currentReceiveTaskCompletionSource = null; + + tcs.TrySetResult(new SocketReceiveResult + { + Buffer = e.Buffer, + ReceivedBytes = e.BytesTransferred, + RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint), + LocalIPAddress = LocalIPAddress + }); + } } public UdpSocket(Socket socket, IpEndPointInfo endPoint) @@ -40,6 +69,8 @@ namespace Emby.Common.Implementations.Net _Socket = socket; _Socket.Connect(NetworkManager.ToIPEndPoint(endPoint)); + + InitReceiveSocketAsyncEventArgs(); } public IpAddressInfo LocalIPAddress @@ -57,12 +88,12 @@ namespace Emby.Common.Implementations.Net var tcs = new TaskCompletionSource(); EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); - var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); - state.TaskCompletionSource = tcs; - cancellationToken.Register(() => tcs.TrySetCanceled()); #if NETSTANDARD1_6 + var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); + state.TaskCompletionSource = tcs; + _Socket.ReceiveFromAsync(new ArraySegment(state.Buffer), SocketFlags.None, state.RemoteEndPoint) .ContinueWith((task, asyncState) => { @@ -74,7 +105,15 @@ namespace Emby.Common.Implementations.Net } }, state); #else - _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); + //var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); + //state.TaskCompletionSource = tcs; + + //_Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); + + _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; + _currentReceiveTaskCompletionSource = tcs; + + var isPending = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); #endif return tcs.Task; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 02a6bf85d..17c57712e 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -275,6 +275,16 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun public int HD { get; set; } } + protected EncodingOptions GetEncodingOptions() + { + return Config.GetConfiguration("encoding"); + } + + private string GetHdHrIdFromChannelId(string channelId) + { + return channelId.Split('_')[1]; + } + private MediaSourceInfo GetMediaSource(TunerHostInfo info, string channelId, ChannelInfo channelInfo, string profile) { int? width = null; @@ -362,14 +372,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun nal = "0"; } - var url = GetApiUrl(info, true) + "/auto/v" + channelId; - - // If raw was used, the tuner doesn't support params - if (!string.IsNullOrWhiteSpace(profile) - && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) - { - url += "?transcode=" + profile; - } + var url = GetApiUrl(info, false); var id = profile; if (string.IsNullOrWhiteSpace(id)) @@ -378,92 +381,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } id += "_" + url.GetMD5().ToString("N"); - var mediaSource = new MediaSourceInfo - { - Path = url, - Protocol = MediaProtocol.Http, - MediaStreams = new List - { - new MediaStream - { - Type = MediaStreamType.Video, - // Set the index to -1 because we don't know the exact index of the video stream within the container - Index = -1, - IsInterlaced = isInterlaced, - Codec = videoCodec, - Width = width, - Height = height, - BitRate = videoBitrate, - NalLengthSize = nal - - }, - new MediaStream - { - Type = MediaStreamType.Audio, - // Set the index to -1 because we don't know the exact index of the audio stream within the container - Index = -1, - Codec = audioCodec, - BitRate = audioBitrate - } - }, - RequiresOpening = true, - RequiresClosing = false, - BufferMs = 0, - Container = "ts", - Id = id, - SupportsDirectPlay = false, - SupportsDirectStream = true, - SupportsTranscoding = true, - IsInfiniteStream = true - }; - - mediaSource.InferTotalBitrate(); - - return mediaSource; - } - - protected EncodingOptions GetEncodingOptions() - { - return Config.GetConfiguration("encoding"); - } - - private string GetHdHrIdFromChannelId(string channelId) - { - return channelId.Split('_')[1]; - } - - private MediaSourceInfo GetLegacyMediaSource(TunerHostInfo info, string channelId, ChannelInfo channel) - { - int? width = null; - int? height = null; - bool isInterlaced = true; - string videoCodec = null; - string audioCodec = null; - - int? videoBitrate = null; - int? audioBitrate = null; - - if (channel != null) - { - if (string.IsNullOrWhiteSpace(videoCodec)) - { - videoCodec = channel.VideoCodec; - } - audioCodec = channel.AudioCodec; - } - - // normalize - if (string.Equals(videoCodec, "mpeg2", StringComparison.OrdinalIgnoreCase)) - { - videoCodec = "mpeg2video"; - } - - string nal = null; - - var url = GetApiUrl(info, false); - var id = channelId; - id += "_" + url.GetMD5().ToString("N"); - var mediaSource = new MediaSourceInfo { Path = url, @@ -527,7 +444,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun if (isLegacyTuner) { - list.Add(GetLegacyMediaSource(info, hdhrId, channelInfo)); + list.Add(GetMediaSource(info, hdhrId, channelInfo, "native")); } else { @@ -579,20 +496,17 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun var hdhomerunChannel = channelInfo as HdHomerunChannelInfo; + var mediaSource = GetMediaSource(info, hdhrId, channelInfo, profile); + var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); + if (hdhomerunChannel != null && hdhomerunChannel.IsLegacyTuner) { - var mediaSource = GetLegacyMediaSource(info, hdhrId, channelInfo); - var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); - return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } else { - var mediaSource = GetMediaSource(info, hdhrId, channelInfo, profile); - //var modelInfo = await GetModelInfo(info, false, cancellationToken).ConfigureAwait(false); - - return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); - //return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); + //return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs index 867e07e87..92000a1b3 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunUdpStream.cs @@ -120,8 +120,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun // send url to start streaming await hdHomerunManager.StartStreaming(remoteAddress, localAddress, localPort, _channelCommands, _numTuners, cancellationToken).ConfigureAwait(false); - var timeoutToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(5000).Token, cancellationToken).Token; - var response = await udpClient.ReceiveAsync(timeoutToken).ConfigureAwait(false); _logger.Info("Opened HDHR UDP stream from {0}", remoteAddress); if (!cancellationToken.IsCancellationRequested) @@ -132,8 +130,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun onStarted = () => openTaskCompletionSource.TrySetResult(true); } - var stream = new UdpClientStream(udpClient); - await _multicastStream.CopyUntilCancelled(stream, onStarted, cancellationToken).ConfigureAwait(false); + await _multicastStream.CopyUntilCancelled(udpClient, onStarted, cancellationToken).ConfigureAwait(false); } } catch (OperationCanceledException ex) @@ -158,7 +155,6 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } await hdHomerunManager.StopStreaming().ConfigureAwait(false); - udpClient.Dispose(); _liveStreamTaskCompletionSource.TrySetResult(true); } } @@ -171,127 +167,4 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun return _multicastStream.CopyToAsync(stream); } } - - // This handles the ReadAsync function only of a Stream object - // This is used to wrap a UDP socket into a stream for MulticastStream which only uses ReadAsync - public class UdpClientStream : Stream - { - private static int RtpHeaderBytes = 12; - private static int PacketSize = 1316; - private readonly ISocket _udpClient; - bool disposed; - - public UdpClientStream(ISocket udpClient) : base() - { - _udpClient = udpClient; - } - - public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) - { - if (buffer == null) - throw new ArgumentNullException("buffer"); - - if (offset + count < 0) - throw new ArgumentOutOfRangeException("offset + count must not be negative", "offset+count"); - - if (offset + count > buffer.Length) - throw new ArgumentException("offset + count must not be greater than the length of buffer", "offset+count"); - - if (disposed) - throw new ObjectDisposedException(typeof(UdpClientStream).ToString()); - - // This will always receive a 1328 packet size (PacketSize + RtpHeaderSize) - // The RTP header will be stripped so see how many reads we need to make to fill the buffer. - int numReads = count / PacketSize; - int totalBytesRead = 0; - - for (int i = 0; i < numReads; ++i) - { - var data = await _udpClient.ReceiveAsync(cancellationToken).ConfigureAwait(false); - - var bytesRead = data.ReceivedBytes - RtpHeaderBytes; - - // remove rtp header - Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, buffer, offset, bytesRead); - offset += bytesRead; - totalBytesRead += bytesRead; - } - return totalBytesRead; - } - - protected override void Dispose(bool disposing) - { - disposed = true; - } - - public override bool CanRead - { - get - { - throw new NotImplementedException(); - } - } - - public override bool CanSeek - { - get - { - throw new NotImplementedException(); - } - } - - public override bool CanWrite - { - get - { - throw new NotImplementedException(); - } - } - - public override long Length - { - get - { - throw new NotImplementedException(); - } - } - - public override long Position - { - get - { - throw new NotImplementedException(); - } - - set - { - throw new NotImplementedException(); - } - } - - public override void Flush() - { - throw new NotImplementedException(); - } - - public override int Read(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - - public override long Seek(long offset, SeekOrigin origin) - { - throw new NotImplementedException(); - } - - public override void SetLength(long value) - { - throw new NotImplementedException(); - } - - public override void Write(byte[] buffer, int offset, int count) - { - throw new NotImplementedException(); - } - } } \ No newline at end of file diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs index 90ff36441..e3d0d1eba 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/MulticastStream.cs @@ -7,6 +7,7 @@ using System.Text; using System.Threading; using System.Threading.Tasks; using MediaBrowser.Model.Logging; +using MediaBrowser.Model.Net; namespace Emby.Server.Implementations.LiveTv.TunerHosts { @@ -40,7 +41,52 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts var allStreams = _outputStreams.ToList(); foreach (var stream in allStreams) { - stream.Value.Queue(copy); + stream.Value.Queue(copy, 0, copy.Length); + } + + if (onStarted != null) + { + var onStartedCopy = onStarted; + onStarted = null; + Task.Run(onStartedCopy); + } + } + + else + { + await Task.Delay(100).ConfigureAwait(false); + } + } + } + + private static int RtpHeaderBytes = 12; + public async Task CopyUntilCancelled(ISocket udpClient, Action onStarted, CancellationToken cancellationToken) + { + _cancellationToken = cancellationToken; + + while (!cancellationToken.IsCancellationRequested) + { + var receiveToken = cancellationToken; + + // On the first connection attempt, put a timeout to avoid being stuck indefinitely in the event of failure + if (onStarted != null) + { + receiveToken = CancellationTokenSource.CreateLinkedTokenSource(new CancellationTokenSource(5000).Token, cancellationToken).Token; + } + + var data = await udpClient.ReceiveAsync(receiveToken).ConfigureAwait(false); + var bytesRead = data.ReceivedBytes - RtpHeaderBytes; + + if (bytesRead > 0) + { + byte[] copy = new byte[bytesRead]; + Buffer.BlockCopy(data.Buffer, RtpHeaderBytes, copy, 0, bytesRead); + + var allStreams = _outputStreams.ToList(); + foreach (var stream in allStreams) + { + //stream.Value.Queue(data.Buffer, RtpHeaderBytes, bytesRead); + stream.Value.Queue(copy, 0, copy.Length); } if (onStarted != null) diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs index 7b48ce21a..27dd288a7 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/QueueStream.cs @@ -13,7 +13,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts public class QueueStream { private readonly Stream _outputStream; - private readonly ConcurrentQueue _queue = new ConcurrentQueue(); + private readonly ConcurrentQueue> _queue = new ConcurrentQueue>(); private CancellationToken _cancellationToken; public TaskCompletionSource TaskCompletion { get; private set; } @@ -28,9 +28,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts TaskCompletion = new TaskCompletionSource(); } - public void Queue(byte[] bytes) + public void Queue(byte[] bytes, int offset, int count) { - _queue.Enqueue(bytes); + _queue.Enqueue(new Tuple(bytes, offset, count)); } public void Start(CancellationToken cancellationToken) @@ -39,12 +39,12 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts Task.Run(() => StartInternal()); } - private byte[] Dequeue() + private Tuple Dequeue() { - byte[] bytes; - if (_queue.TryDequeue(out bytes)) + Tuple result; + if (_queue.TryDequeue(out result)) { - return bytes; + return result; } return null; @@ -58,10 +58,10 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts { while (true) { - var bytes = Dequeue(); - if (bytes != null) + var result = Dequeue(); + if (result != null) { - await _outputStream.WriteAsync(bytes, 0, bytes.Length, cancellationToken).ConfigureAwait(false); + await _outputStream.WriteAsync(result.Item1, result.Item2, result.Item3, cancellationToken).ConfigureAwait(false); } else { -- cgit v1.2.3 From caaa906604c759e6899ebf6be6f5ac4f9845db84 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 15:00:35 -0400 Subject: update socket methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 248 +++++++++------------------ Emby.Server.Core/IO/LibraryMonitor.cs | 11 -- Emby.Server.Implementations/Udp/UdpServer.cs | 26 ++- MediaBrowser.Model/Net/ISocket.cs | 1 + RSSDP/SsdpCommunicationsServer.cs | 10 +- 5 files changed, 103 insertions(+), 193 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 45cb42ecd..94d073bd2 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -19,12 +19,20 @@ namespace Emby.Common.Implementations.Net private Socket _Socket; private int _LocalPort; - private SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() + private readonly SocketAsyncEventArgs _receiveSocketAsyncEventArgs = new SocketAsyncEventArgs() + { + SocketFlags = SocketFlags.None + }; + + private readonly SocketAsyncEventArgs _sendSocketAsyncEventArgs = new SocketAsyncEventArgs() { SocketFlags = SocketFlags.None }; private TaskCompletionSource _currentReceiveTaskCompletionSource; + private TaskCompletionSource _currentSendTaskCompletionSource; + + private readonly SemaphoreSlim _sendLock = new SemaphoreSlim(1, 1); public UdpSocket(Socket socket, int localPort, IPAddress ip) { @@ -41,9 +49,13 @@ namespace Emby.Common.Implementations.Net private void InitReceiveSocketAsyncEventArgs() { - var buffer = new byte[8192]; - _receiveSocketAsyncEventArgs.SetBuffer(buffer, 0, buffer.Length); + var receiveBuffer = new byte[8192]; + _receiveSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length); _receiveSocketAsyncEventArgs.Completed += _receiveSocketAsyncEventArgs_Completed; + + var sendBuffer = new byte[8192]; + _sendSocketAsyncEventArgs.SetBuffer(sendBuffer, 0, sendBuffer.Length); + _sendSocketAsyncEventArgs.Completed += _sendSocketAsyncEventArgs_Completed; } private void _receiveSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) @@ -53,13 +65,38 @@ namespace Emby.Common.Implementations.Net { _currentReceiveTaskCompletionSource = null; - tcs.TrySetResult(new SocketReceiveResult + if (e.SocketError == SocketError.Success) { - Buffer = e.Buffer, - ReceivedBytes = e.BytesTransferred, - RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint), - LocalIPAddress = LocalIPAddress - }); + tcs.TrySetResult(new SocketReceiveResult + { + Buffer = e.Buffer, + ReceivedBytes = e.BytesTransferred, + RemoteEndPoint = ToIpEndPointInfo(e.RemoteEndPoint as IPEndPoint), + LocalIPAddress = LocalIPAddress + }); + } + else + { + tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + } + } + } + + private void _sendSocketAsyncEventArgs_Completed(object sender, SocketAsyncEventArgs e) + { + var tcs = _currentSendTaskCompletionSource; + if (tcs != null) + { + _currentSendTaskCompletionSource = null; + + if (e.SocketError == SocketError.Success) + { + tcs.TrySetResult(e.BytesTransferred); + } + else + { + tcs.TrySetException(new Exception("SocketError: " + e.SocketError)); + } } } @@ -79,8 +116,6 @@ namespace Emby.Common.Implementations.Net private set; } - #region ISocket Members - public Task ReceiveAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); @@ -90,31 +125,15 @@ namespace Emby.Common.Implementations.Net EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); cancellationToken.Register(() => tcs.TrySetCanceled()); -#if NETSTANDARD1_6 - var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); - state.TaskCompletionSource = tcs; - - _Socket.ReceiveFromAsync(new ArraySegment(state.Buffer), SocketFlags.None, state.RemoteEndPoint) - .ContinueWith((task, asyncState) => - { - if (task.Status != TaskStatus.Faulted) - { - var receiveState = asyncState as AsyncReceiveState; - receiveState.RemoteEndPoint = task.Result.RemoteEndPoint; - ProcessResponse(receiveState, () => task.Result.ReceivedBytes, LocalIPAddress); - } - }, state); -#else - //var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); - //state.TaskCompletionSource = tcs; - - //_Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); - _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; _currentReceiveTaskCompletionSource = tcs; - var isPending = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); -#endif + var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); + + if (!willRaiseEvent) + { + _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs); + } return tcs.Task; } @@ -126,60 +145,42 @@ namespace Emby.Common.Implementations.Net if (buffer == null) throw new ArgumentNullException("messageData"); if (endPoint == null) throw new ArgumentNullException("endPoint"); - var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); + cancellationToken.ThrowIfCancellationRequested(); + + var tcs = new TaskCompletionSource(); + + cancellationToken.Register(() => tcs.TrySetCanceled()); + + _sendSocketAsyncEventArgs.SetBuffer(buffer, 0, size); + _sendSocketAsyncEventArgs.RemoteEndPoint = NetworkManager.ToIPEndPoint(endPoint); + _currentSendTaskCompletionSource = tcs; -#if NETSTANDARD1_6 + var willRaiseEvent = _Socket.SendAsync(_sendSocketAsyncEventArgs); - if (size != buffer.Length) + if (!willRaiseEvent) { - byte[] copy = new byte[size]; - Buffer.BlockCopy(buffer, 0, copy, 0, size); - buffer = copy; + _sendSocketAsyncEventArgs_Completed(this, _sendSocketAsyncEventArgs); } - cancellationToken.ThrowIfCancellationRequested(); + return tcs.Task; + } + + public async Task SendWithLockAsync(byte[] buffer, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken) + { + ThrowIfDisposed(); - _Socket.SendTo(buffer, ipEndPoint); - return Task.FromResult(true); -#else - var taskSource = new TaskCompletionSource(); + await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { - _Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, ipEndPoint, result => - { - if (cancellationToken.IsCancellationRequested) - { - taskSource.TrySetCanceled(); - return; - } - try - { - _Socket.EndSend(result); - taskSource.TrySetResult(true); - } - catch (Exception ex) - { - taskSource.TrySetException(ex); - } - - }, null); + await SendAsync(buffer, size, endPoint, cancellationToken).ConfigureAwait(false); } - catch (Exception ex) + finally { - taskSource.TrySetException(ex); + _sendLock.Release(); } - - //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port)); - - return taskSource.Task; -#endif } - #endregion - - #region Overrides - protected override void Dispose(bool disposing) { if (disposing) @@ -187,44 +188,19 @@ namespace Emby.Common.Implementations.Net var socket = _Socket; if (socket != null) socket.Dispose(); - } - } - - #endregion - #region Private Methods + _sendLock.Dispose(); - private static void ProcessResponse(AsyncReceiveState state, Func receiveData, IpAddressInfo localIpAddress) - { - try - { - var bytesRead = receiveData(); - - var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.TrySetResult( - new SocketReceiveResult - { - Buffer = state.Buffer, - ReceivedBytes = bytesRead, - RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), - LocalIPAddress = localIpAddress - } - ); - } - catch (ObjectDisposedException) - { - state.TaskCompletionSource.TrySetCanceled(); - } - catch (SocketException se) - { - if (se.SocketErrorCode != SocketError.Interrupted && se.SocketErrorCode != SocketError.OperationAborted && se.SocketErrorCode != SocketError.Shutdown) - state.TaskCompletionSource.TrySetException(se); - else - state.TaskCompletionSource.TrySetCanceled(); - } - catch (Exception ex) - { - state.TaskCompletionSource.TrySetException(ex); + var tcs = _currentReceiveTaskCompletionSource; + if (tcs != null) + { + tcs.TrySetCanceled(); + } + var sendTcs = _currentSendTaskCompletionSource; + if (sendTcs != null) + { + sendTcs.TrySetCanceled(); + } } } @@ -237,59 +213,5 @@ namespace Emby.Common.Implementations.Net return NetworkManager.ToIpEndPointInfo(endpoint); } - - private void ProcessResponse(IAsyncResult asyncResult) - { -#if NET46 - var state = asyncResult.AsyncState as AsyncReceiveState; - try - { - var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint); - - var ipEndPoint = state.RemoteEndPoint as IPEndPoint; - state.TaskCompletionSource.TrySetResult( - new SocketReceiveResult - { - Buffer = state.Buffer, - ReceivedBytes = bytesRead, - RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), - LocalIPAddress = LocalIPAddress - } - ); - } - catch (ObjectDisposedException) - { - state.TaskCompletionSource.TrySetCanceled(); - } - catch (Exception ex) - { - state.TaskCompletionSource.TrySetException(ex); - } -#endif - } - - #endregion - - #region Private Classes - - private class AsyncReceiveState - { - public AsyncReceiveState(Socket socket, EndPoint remoteEndPoint) - { - this.Socket = socket; - this.RemoteEndPoint = remoteEndPoint; - } - - public EndPoint RemoteEndPoint; - public byte[] Buffer = new byte[8192]; - - public Socket Socket { get; private set; } - - public TaskCompletionSource TaskCompletionSource { get; set; } - - } - - #endregion - } } diff --git a/Emby.Server.Core/IO/LibraryMonitor.cs b/Emby.Server.Core/IO/LibraryMonitor.cs index 4df9b930e..e1e3186c3 100644 --- a/Emby.Server.Core/IO/LibraryMonitor.cs +++ b/Emby.Server.Core/IO/LibraryMonitor.cs @@ -421,17 +421,6 @@ namespace Emby.Server.Core.IO var path = e.FullPath; - // For deletes, use the parent path - if (e.ChangeType == WatcherChangeTypes.Deleted) - { - var parentPath = Path.GetDirectoryName(path); - - if (!string.IsNullOrWhiteSpace(parentPath)) - { - path = parentPath; - } - } - ReportFileSystemChanged(path); } catch (Exception ex) diff --git a/Emby.Server.Implementations/Udp/UdpServer.cs b/Emby.Server.Implementations/Udp/UdpServer.cs index bb303d8fa..21ef3cab6 100644 --- a/Emby.Server.Implementations/Udp/UdpServer.cs +++ b/Emby.Server.Implementations/Udp/UdpServer.cs @@ -203,19 +203,6 @@ namespace Emby.Server.Implementations.Udp GC.SuppressFinalize(this); } - /// - /// Stops this instance. - /// - public void Stop() - { - _isDisposed = true; - - if (_udpClient != null) - { - _udpClient.Dispose(); - } - } - /// /// Releases unmanaged and - optionally - managed resources. /// @@ -224,7 +211,12 @@ namespace Emby.Server.Implementations.Udp { if (dispose) { - Stop(); + _isDisposed = true; + + if (_udpClient != null) + { + _udpClient.Dispose(); + } } } @@ -247,9 +239,13 @@ namespace Emby.Server.Implementations.Udp try { - await _udpClient.SendAsync(bytes, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false); + await _udpClient.SendWithLockAsync(bytes, bytes.Length, remoteEndPoint, CancellationToken.None).ConfigureAwait(false); _logger.Info("Udp message sent to {0}", remoteEndPoint); + } + catch (OperationCanceledException) + { + } catch (Exception ex) { diff --git a/MediaBrowser.Model/Net/ISocket.cs b/MediaBrowser.Model/Net/ISocket.cs index 90070b128..61fc0e28b 100644 --- a/MediaBrowser.Model/Net/ISocket.cs +++ b/MediaBrowser.Model/Net/ISocket.cs @@ -24,5 +24,6 @@ namespace MediaBrowser.Model.Net /// Sends a UDP message to a particular end point (uni or multicast). /// Task SendAsync(byte[] buffer, int bytes, IpEndPointInfo endPoint, CancellationToken cancellationToken); + Task SendWithLockAsync(byte[] buffer, int bytes, IpEndPointInfo endPoint, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/RSSDP/SsdpCommunicationsServer.cs b/RSSDP/SsdpCommunicationsServer.cs index 99e3969aa..cc464e689 100644 --- a/RSSDP/SsdpCommunicationsServer.cs +++ b/RSSDP/SsdpCommunicationsServer.cs @@ -177,11 +177,15 @@ namespace Rssdp.Infrastructure { try { - await socket.SendAsync(messageData, messageData.Length, destination, cancellationToken).ConfigureAwait(false); + await socket.SendWithLockAsync(messageData, messageData.Length, destination, cancellationToken).ConfigureAwait(false); } catch (ObjectDisposedException) { + } + catch (OperationCanceledException) + { + } catch (Exception ex) { @@ -341,11 +345,9 @@ namespace Rssdp.Infrastructure foreach (var socket in sockets) { - await socket.SendAsync(messageData, messageData.Length, destination, cancellationToken).ConfigureAwait(false); + await SendFromSocket(socket, messageData, destination, cancellationToken).ConfigureAwait(false); } } - - ThrowIfDisposed(); } private ISocket ListenForBroadcastsAsync() -- cgit v1.2.3 From 8a68c2383866c7e0b21dc164f1126bd78699c1fb Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 15:54:50 -0400 Subject: update socket send functions --- Emby.Common.Implementations/Net/UdpSocket.cs | 77 ++++++++++++++++++---- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 8 ++- 2 files changed, 72 insertions(+), 13 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 94d073bd2..834f0a05c 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -145,31 +145,84 @@ namespace Emby.Common.Implementations.Net if (buffer == null) throw new ArgumentNullException("messageData"); if (endPoint == null) throw new ArgumentNullException("endPoint"); - cancellationToken.ThrowIfCancellationRequested(); + var ipEndPoint = NetworkManager.ToIPEndPoint(endPoint); - var tcs = new TaskCompletionSource(); +#if NETSTANDARD1_6 - cancellationToken.Register(() => tcs.TrySetCanceled()); + if (size != buffer.Length) + { + byte[] copy = new byte[size]; + Buffer.BlockCopy(buffer, 0, copy, 0, size); + buffer = copy; + } - _sendSocketAsyncEventArgs.SetBuffer(buffer, 0, size); - _sendSocketAsyncEventArgs.RemoteEndPoint = NetworkManager.ToIPEndPoint(endPoint); - _currentSendTaskCompletionSource = tcs; + cancellationToken.ThrowIfCancellationRequested(); - var willRaiseEvent = _Socket.SendAsync(_sendSocketAsyncEventArgs); + _Socket.SendTo(buffer, ipEndPoint); + return Task.FromResult(true); +#else + var taskSource = new TaskCompletionSource(); - if (!willRaiseEvent) + try + { + _Socket.BeginSendTo(buffer, 0, size, SocketFlags.None, ipEndPoint, result => + { + if (cancellationToken.IsCancellationRequested) + { + taskSource.TrySetCanceled(); + return; + } + try + { + _Socket.EndSend(result); + taskSource.TrySetResult(true); + } + catch (Exception ex) + { + taskSource.TrySetException(ex); + } + + }, null); + } + catch (Exception ex) { - _sendSocketAsyncEventArgs_Completed(this, _sendSocketAsyncEventArgs); + taskSource.TrySetException(ex); } - return tcs.Task; + //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port)); + + return taskSource.Task; +#endif + //ThrowIfDisposed(); + + //if (buffer == null) throw new ArgumentNullException("messageData"); + //if (endPoint == null) throw new ArgumentNullException("endPoint"); + + //cancellationToken.ThrowIfCancellationRequested(); + + //var tcs = new TaskCompletionSource(); + + //cancellationToken.Register(() => tcs.TrySetCanceled()); + + //_sendSocketAsyncEventArgs.SetBuffer(buffer, 0, size); + //_sendSocketAsyncEventArgs.RemoteEndPoint = NetworkManager.ToIPEndPoint(endPoint); + //_currentSendTaskCompletionSource = tcs; + + //var willRaiseEvent = _Socket.SendAsync(_sendSocketAsyncEventArgs); + + //if (!willRaiseEvent) + //{ + // _sendSocketAsyncEventArgs_Completed(this, _sendSocketAsyncEventArgs); + //} + + //return tcs.Task; } public async Task SendWithLockAsync(byte[] buffer, int size, IpEndPointInfo endPoint, CancellationToken cancellationToken) { ThrowIfDisposed(); - await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); + //await _sendLock.WaitAsync(cancellationToken).ConfigureAwait(false); try { @@ -177,7 +230,7 @@ namespace Emby.Common.Implementations.Net } finally { - _sendLock.Release(); + //_sendLock.Release(); } } diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index 17c57712e..f0529c596 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -505,7 +505,13 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun } else { - //return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + var enableHttpStream = false; + + if (enableHttpStream) + { + return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + } + return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } } -- cgit v1.2.3 From 5477bb95d9bf994f9c68bf5384d26db41c82d5de Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 19:36:52 -0400 Subject: update socket methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index 834f0a05c..fc23422d7 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -49,7 +49,7 @@ namespace Emby.Common.Implementations.Net private void InitReceiveSocketAsyncEventArgs() { - var receiveBuffer = new byte[8192]; + var receiveBuffer = new byte[81920]; _receiveSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length); _receiveSocketAsyncEventArgs.Completed += _receiveSocketAsyncEventArgs_Completed; @@ -128,11 +128,18 @@ namespace Emby.Common.Implementations.Net _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; _currentReceiveTaskCompletionSource = tcs; - var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); + try + { + var willRaiseEvent = _Socket.ReceiveFromAsync(_receiveSocketAsyncEventArgs); - if (!willRaiseEvent) + if (!willRaiseEvent) + { + _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs); + } + } + catch (Exception ex) { - _receiveSocketAsyncEventArgs_Completed(this, _receiveSocketAsyncEventArgs); + tcs.TrySetException(ex); } return tcs.Task; @@ -189,8 +196,6 @@ namespace Emby.Common.Implementations.Net taskSource.TrySetException(ex); } - //_Socket.SendTo(messageData, new System.Net.IPEndPoint(IPAddress.Parse(RemoteEndPoint.IPAddress), RemoteEndPoint.Port)); - return taskSource.Task; #endif //ThrowIfDisposed(); -- cgit v1.2.3 From 1a5a0d2cbbf70d5e62fdc8d371504010c7ed089c Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 19:53:50 -0400 Subject: update socket methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 58 +++++++++++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index fc23422d7..e44b484cb 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -119,10 +119,17 @@ namespace Emby.Common.Implementations.Net public Task ReceiveAsync(CancellationToken cancellationToken) { ThrowIfDisposed(); - var tcs = new TaskCompletionSource(); - EndPoint receivedFromEndPoint = new IPEndPoint(IPAddress.Any, 0); + + var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); + state.TaskCompletionSource = tcs; + +#if NET46 + _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); + return tcs.Task; +#endif + cancellationToken.Register(() => tcs.TrySetCanceled()); _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; @@ -271,5 +278,52 @@ namespace Emby.Common.Implementations.Net return NetworkManager.ToIpEndPointInfo(endpoint); } + + private void ProcessResponse(IAsyncResult asyncResult) + { +#if NET46 + var state = asyncResult.AsyncState as AsyncReceiveState; + try + { + var bytesRead = state.Socket.EndReceiveFrom(asyncResult, ref state.RemoteEndPoint); + + var ipEndPoint = state.RemoteEndPoint as IPEndPoint; + state.TaskCompletionSource.SetResult( + new SocketReceiveResult + { + Buffer = state.Buffer, + ReceivedBytes = bytesRead, + RemoteEndPoint = ToIpEndPointInfo(ipEndPoint), + LocalIPAddress = LocalIPAddress + } + ); + } + catch (ObjectDisposedException) + { + state.TaskCompletionSource.SetCanceled(); + } + catch (Exception ex) + { + state.TaskCompletionSource.SetException(ex); + } +#endif + } + + private class AsyncReceiveState + { + public AsyncReceiveState(Socket socket, EndPoint remoteEndPoint) + { + this.Socket = socket; + this.RemoteEndPoint = remoteEndPoint; + } + + public EndPoint RemoteEndPoint; + public byte[] Buffer = new byte[8192]; + + public Socket Socket { get; private set; } + + public TaskCompletionSource TaskCompletionSource { get; set; } + + } } } -- cgit v1.2.3 From e004e6650099d3179da996fcccf29ffb65081c09 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Sun, 26 Mar 2017 20:27:29 -0400 Subject: update tuner methods --- Emby.Common.Implementations/Net/UdpSocket.cs | 5 ---- .../LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs | 27 +++++++++++++++++----- 2 files changed, 21 insertions(+), 11 deletions(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index e44b484cb..a97b2e668 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -125,11 +125,6 @@ namespace Emby.Common.Implementations.Net var state = new AsyncReceiveState(_Socket, receivedFromEndPoint); state.TaskCompletionSource = tcs; -#if NET46 - _Socket.BeginReceiveFrom(state.Buffer, 0, state.Buffer.Length, SocketFlags.None, ref state.RemoteEndPoint, ProcessResponse, state); - return tcs.Task; -#endif - cancellationToken.Register(() => tcs.TrySetCanceled()); _receiveSocketAsyncEventArgs.RemoteEndPoint = receivedFromEndPoint; diff --git a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs index f0529c596..2fac96169 100644 --- a/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs +++ b/Emby.Server.Implementations/LiveTv/TunerHosts/HdHomerun/HdHomerunHost.cs @@ -20,6 +20,7 @@ using MediaBrowser.Controller.Configuration; using MediaBrowser.Controller.MediaEncoding; using MediaBrowser.Model.Configuration; using MediaBrowser.Model.Net; +using MediaBrowser.Model.System; namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { @@ -30,8 +31,9 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun private readonly IServerApplicationHost _appHost; private readonly ISocketFactory _socketFactory; private readonly INetworkManager _networkManager; + private readonly IEnvironmentInfo _environment; - public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager) + public HdHomerunHost(IServerConfigurationManager config, ILogger logger, IJsonSerializer jsonSerializer, IMediaEncoder mediaEncoder, IHttpClient httpClient, IFileSystem fileSystem, IServerApplicationHost appHost, ISocketFactory socketFactory, INetworkManager networkManager, IEnvironmentInfo environment) : base(config, logger, jsonSerializer, mediaEncoder) { _httpClient = httpClient; @@ -39,6 +41,7 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun _appHost = appHost; _socketFactory = socketFactory; _networkManager = networkManager; + _environment = environment; } public string Name @@ -503,17 +506,29 @@ namespace Emby.Server.Implementations.LiveTv.TunerHosts.HdHomerun { return new HdHomerunUdpStream(mediaSource, streamId, new LegacyHdHomerunChannelCommands(hdhomerunChannel.Url), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } - else + + // The UDP method is not working reliably on OSX, and on BSD it hasn't been tested yet + var enableHttpStream = _environment.OperatingSystem == OperatingSystem.OSX || + _environment.OperatingSystem == OperatingSystem.BSD; + + if (enableHttpStream) { - var enableHttpStream = false; + mediaSource.Protocol = MediaProtocol.Http; - if (enableHttpStream) + var httpUrl = GetApiUrl(info, true) + "/auto/v" + hdhrId; + + // If raw was used, the tuner doesn't support params + if (!string.IsNullOrWhiteSpace(profile) + && !string.Equals(profile, "native", StringComparison.OrdinalIgnoreCase)) { - return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); + httpUrl += "?transcode=" + profile; } + mediaSource.Path = httpUrl; - return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); + return new HdHomerunHttpStream(mediaSource, streamId, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost); } + + return new HdHomerunUdpStream(mediaSource, streamId, new HdHomerunChannelCommands(hdhomerunChannel.Number), modelInfo.TunerCount, _fileSystem, _httpClient, Logger, Config.ApplicationPaths, _appHost, _socketFactory, _networkManager); } public async Task Validate(TunerHostInfo info) -- cgit v1.2.3 From 2dbe162e45269df00fb44fbe5340b72758399307 Mon Sep 17 00:00:00 2001 From: Luke Pulverenti Date: Tue, 28 Mar 2017 13:32:24 -0400 Subject: revert buffer size --- Emby.Common.Implementations/Net/UdpSocket.cs | 2 +- MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs | 11 +++++++++++ 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'Emby.Common.Implementations') diff --git a/Emby.Common.Implementations/Net/UdpSocket.cs b/Emby.Common.Implementations/Net/UdpSocket.cs index a97b2e668..f9181eb6a 100644 --- a/Emby.Common.Implementations/Net/UdpSocket.cs +++ b/Emby.Common.Implementations/Net/UdpSocket.cs @@ -49,7 +49,7 @@ namespace Emby.Common.Implementations.Net private void InitReceiveSocketAsyncEventArgs() { - var receiveBuffer = new byte[81920]; + var receiveBuffer = new byte[8192]; _receiveSocketAsyncEventArgs.SetBuffer(receiveBuffer, 0, receiveBuffer.Length); _receiveSocketAsyncEventArgs.Completed += _receiveSocketAsyncEventArgs_Completed; diff --git a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs index 5eaab4110..b7b31509c 100644 --- a/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs +++ b/MediaBrowser.Controller/MediaEncoding/EncodingHelper.cs @@ -164,6 +164,10 @@ namespace MediaBrowser.Controller.MediaEncoding { return null; } + if (string.Equals(container, "mts", StringComparison.OrdinalIgnoreCase)) + { + return null; + } if (string.Equals(container, "vob", StringComparison.OrdinalIgnoreCase)) { return null; @@ -1709,6 +1713,13 @@ namespace MediaBrowser.Controller.MediaEncoding return "-c:v h264_qsv "; } break; + //case "hevc": + //case "h265": + // if (_mediaEncoder.SupportsDecoder("hevc_qsv")) + // { + // return "-c:v hevc_qsv "; + // } + // break; case "mpeg2video": if (_mediaEncoder.SupportsDecoder("mpeg2_qsv")) { -- cgit v1.2.3