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 + 2 files changed, 43 insertions(+), 1 deletion(-) (limited to 'Emby.Common.Implementations/Net') 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); -- 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/Net') 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/Net') 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/Net') 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 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/Net') 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/Net') 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/Net') 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/Net') 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/Net') 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/Net') 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/Net') 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