From 82483ee249470d19a3f15a5d2814d21404060b9a Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Sat, 16 Dec 2023 14:15:47 +0300 Subject: [PATCH 01/10] rtspserver: Allow to configure RTSP client inactivity timeout --- RtspServer/RtspServer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/RtspServer/RtspServer.cs b/RtspServer/RtspServer.cs index fd70f659..f006cf77 100644 --- a/RtspServer/RtspServer.cs +++ b/RtspServer/RtspServer.cs @@ -646,16 +646,16 @@ public void RemoveRequestHandler(RtspMethod method) //Todo //Allow for joining of server instances to support multiple end points. - public RtspServer(AddressFamily addressFamily = AddressFamily.InterNetwork, int listenPort = DefaultPort, bool shouldDispose = true) - : this(new IPEndPoint(Media.Common.Extensions.Socket.SocketExtensions.GetFirstUnicastIPAddress(addressFamily), listenPort), shouldDispose) { } + public RtspServer(AddressFamily addressFamily, int listenPort, TimeSpan? inactivityTimeout = null, bool shouldDispose = true) + : this(new IPEndPoint(Media.Common.Extensions.Socket.SocketExtensions.GetFirstUnicastIPAddress(addressFamily), listenPort), inactivityTimeout, shouldDispose) { } - public RtspServer(IPAddress listenAddress, int listenPort, bool shouldDispose = true) - : this(new IPEndPoint(listenAddress, listenPort), shouldDispose) { } + public RtspServer(IPAddress listenAddress, int listenPort, TimeSpan? inactivityTimeout = null, bool shouldDispose = true) + : this(new IPEndPoint(listenAddress, listenPort), inactivityTimeout, shouldDispose) { } - public RtspServer(IPEndPoint listenEndPoint, bool shouldDispose = true) + public RtspServer(IPEndPoint listenEndPoint, TimeSpan? inactivityTimeout = null, bool shouldDispose = true) : base(shouldDispose) { - RtspClientInactivityTimeout = TimeSpan.FromSeconds(60); + RtspClientInactivityTimeout = inactivityTimeout ?? TimeSpan.FromSeconds(60); //Check syntax of Server header to ensure allowed format... ServerName = "ASTI Media Server RTSP\\" + Version.ToString(RtspMessage.VersionFormat, System.Globalization.CultureInfo.InvariantCulture); From 4070e2184c9a2ed6d8e86d6c64b1d1453c56fa75 Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Sat, 16 Dec 2023 14:20:16 +0300 Subject: [PATCH 02/10] rtspserver: Use documented way to enable unicast port reuse for socket --- Common/Extensions/SocketExtensions.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/Common/Extensions/SocketExtensions.cs b/Common/Extensions/SocketExtensions.cs index cf41380a..67eba78b 100644 --- a/Common/Extensions/SocketExtensions.cs +++ b/Common/Extensions/SocketExtensions.cs @@ -963,15 +963,9 @@ public static void DisableAddressReuse(System.Net.Sockets.Socket socket, bool ex #region UnicastPortReuse - //Notes 4.6 has ReuseUnicastPort - - private const int ReuseUnicastPort = 0x3007; // 12295 - - private static readonly System.Net.Sockets.SocketOptionName ReuseUnicastPortOption = (System.Net.Sockets.SocketOptionName)ReuseUnicastPort; - public static void SetUnicastPortReuse(System.Net.Sockets.Socket socket, int value) { - socket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, ReuseUnicastPortOption, value); + socket.SetSocketOption(System.Net.Sockets.SocketOptionLevel.Socket, System.Net.Sockets.SocketOptionName.ReuseUnicastPort, value); } public static void DisableUnicastPortReuse(System.Net.Sockets.Socket socket) From 15ba47cccb031774cbc1dc41adc60c0cef43aeeb Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Thu, 14 Dec 2023 22:10:37 +0300 Subject: [PATCH 03/10] rtspserver: Thread-safe access in maintaining thread --- RtspServer/RtspServer.cs | 53 +++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/RtspServer/RtspServer.cs b/RtspServer/RtspServer.cs index f006cf77..dc699058 100644 --- a/RtspServer/RtspServer.cs +++ b/RtspServer/RtspServer.cs @@ -255,17 +255,35 @@ internal static void ConfigureRtspServerSocket(Socket socket, out int interFrame private Thread m_ServerThread; /// - /// Indicates to the ServerThread a stop has been requested + /// Indicates to the ServerThread a stop has been requested. /// - private bool m_StopRequested, m_Maintaining; + private int m_StopRequested, m_Maintaining; //Handles the Restarting of streams which needs to be and disconnects clients which are inactive. - internal Timer m_Maintainer; + internal volatile Timer m_Maintainer; #endregion #region Propeties + /// + /// Is stop requested? Thread-safe as required. + /// + private bool IsStopRequested + { + get => Volatile.Read(ref m_StopRequested) == 1; + set => Interlocked.Exchange(ref m_StopRequested, value ? 1 : 0); + } + + /// + /// Is maintaining server? Thread-safe as required. + /// + private bool IsMaintaining + { + get => Volatile.Read(ref m_Maintaining) == 1; + set => Interlocked.Exchange(ref m_Maintaining, value ? 1 : 0); + } + public Media.Rtsp.Server.RtspStreamArchiver Archiver { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] @@ -427,7 +445,7 @@ public bool IsRunning get { return IsDisposed is false && - m_StopRequested is false && + IsStopRequested is false && m_Started.HasValue && m_ServerThread is not null; } @@ -1210,7 +1228,7 @@ public virtual async Task StartAsync(bool allowAddressReuse = false, bool allowP if (IsRunning) return; //Allowed to run - m_Maintaining = m_StopRequested = false; + IsMaintaining = IsStopRequested = false; //Indicate start was called Common.ILoggingExtensions.Log(Logger, "Server Started @ " + DateTime.UtcNow); @@ -1262,7 +1280,7 @@ public virtual async Task StartAsync(bool allowAddressReuse = false, bool allowP //Start it m_ServerThread.Start(); - //Timer for maintaince ( one quarter of the ticks) + //Timer for maintaince (one quarter of the ticks) m_Maintainer = new Timer(MaintainServer, null, TimeSpan.FromTicks(RtspClientInactivityTimeout.Ticks >> 2), @@ -1290,11 +1308,11 @@ public virtual async Task StartAsync(bool allowAddressReuse = false, bool allowP /// Reserved internal virtual void MaintainServer(object state = null) { - if (m_Maintaining || IsDisposed || m_StopRequested) return; + if (IsMaintaining || IsDisposed || IsStopRequested) return; - if (IsRunning && m_Maintaining is false) + if (IsRunning && IsMaintaining is false) { - m_Maintaining = true; + IsMaintaining = true; try { @@ -1315,7 +1333,7 @@ internal virtual void MaintainServer(object state = null) MediaStreams.AsParallel().ForAll(s => s.TrySetLogger(Logger)); - m_Maintaining = false; + IsMaintaining = false; if (IsRunning && m_Maintainer is not null) { @@ -1336,7 +1354,7 @@ internal virtual void MaintainServer(object state = null) { Common.ILoggingExtensions.LogException(Logger, ex); - m_Maintaining = m_Maintainer is not null; + IsMaintaining = m_Maintainer is not null; } } } @@ -1353,7 +1371,7 @@ public virtual async Task StopAsync(bool leaveOpen = false) if (IsRunning is false) return; //Stop listening for new clients - m_StopRequested = true; + IsStopRequested = true; Common.ILoggingExtensions.Log(Logger, "@Stop - StopRequested"); Common.ILoggingExtensions.Log(Logger, "Connected Clients:" + ActiveConnections); @@ -1415,7 +1433,7 @@ internal virtual async Task StartStreamsAsync() foreach (Media.Rtsp.Server.IMedia stream in MediaStreams) { - if (m_StopRequested) return; + if (IsStopRequested) return; streamTasks.Add(StartStreamAsync(stream)); } @@ -1509,9 +1527,10 @@ internal void AcceptLoop() //While running while (IsRunning) { - if (m_StopRequested) + if (IsStopRequested) break; - else if (m_Sessions.Count < m_MaximumSessions) + + if (m_Sessions.Count < m_MaximumSessions) { //If not already accepting if (lastAccept is null) @@ -1560,7 +1579,7 @@ internal void AcceptLoop() { Common.ILoggingExtensions.LogException(Logger, ex); - if (m_StopRequested) return; + if (IsStopRequested) return; goto Begin; } @@ -2245,7 +2264,7 @@ generated URLs. //Then it would also be easier to make /audio only passwords etc. //When stopping only handle teardown and keep alives - if (m_StopRequested && + if (IsStopRequested && (request.ContainsHeader(RtspHeaders.Connection) is false || (request.RtspMethod != RtspMethod.TEARDOWN && request.RtspMethod != RtspMethod.GET_PARAMETER && request.RtspMethod != RtspMethod.OPTIONS))) { From 2c886912d853dfa3bd5c9ded5b1b4665c423533b Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Thu, 14 Dec 2023 22:50:38 +0300 Subject: [PATCH 04/10] common: Use Interlocked API to read int for performance long requires special lock on x86, and slower in general. Profiler shows a lot of time in Disposed checks. --- Common/Classes/Disposables/BaseDisposable.cs | 14 +++++--------- .../Disposables/SuppressedFinalizerDisposable.cs | 2 +- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/Common/Classes/Disposables/BaseDisposable.cs b/Common/Classes/Disposables/BaseDisposable.cs index 37d9c2f2..3bc0ddd8 100644 --- a/Common/Classes/Disposables/BaseDisposable.cs +++ b/Common/Classes/Disposables/BaseDisposable.cs @@ -81,7 +81,7 @@ public abstract class BaseDisposable : IDisposed, IAsyncDisposable /// /// [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - internal static long RetrieveState(BaseDisposable bd) { return bd.State; } + internal static int RetrieveState(BaseDisposable bd) { return bd.State; } /// /// Indicates the @@ -89,7 +89,7 @@ public abstract class BaseDisposable : IDisposed, IAsyncDisposable /// /// [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - internal static bool IsDestructing(BaseDisposable bd, ref long state) { return (state = RetrieveState(bd)) > Disposed; } + internal static bool IsDestructing(BaseDisposable bd, ref int state) { return (state = RetrieveState(bd)) > Disposed; } /// /// If the sender is of the type then will be called to dispose the instance immediately. @@ -133,7 +133,7 @@ public static void SetShouldDispose(BaseDisposable toDispose, bool value, bool c /// The sign bit of the integer value is the only 'confusing' part about this and it must be understood because the Interlocked methods are CLS Compliant and do not expose unsigned counterparts. /// See the remarks section above for more clarity. /// - private long State; // = Undisposed; (Todo, internal protected and can remove Statics.. or new private protected and...) + private int State; // = Undisposed; (Todo, internal protected and can remove Statics.. or new private protected and...) #endregion @@ -188,9 +188,7 @@ protected internal bool IsUndisposed [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] get { - //return System.Threading.Thread.VolatileRead(ref State) == Undisposed; - //return (System.Threading.Interlocked.Read(ref State) & int.MaxValue).Equals(Undisposed); - return System.Threading.Interlocked.Read(ref State).Equals(Undisposed); + return System.Threading.Volatile.Read(ref State) == Undisposed; } } @@ -202,9 +200,7 @@ internal bool IsFinalized [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] get { - //return System.Threading.Thread.VolatileRead(ref State) == Finalized; - //return (System.Threading.Interlocked.Read(ref State) & int.MaxValue).Equals(Finalized); - return System.Threading.Interlocked.Read(ref State).Equals(Finalized); + return System.Threading.Volatile.Read(ref State) == Finalized; } } diff --git a/Common/Classes/Disposables/SuppressedFinalizerDisposable.cs b/Common/Classes/Disposables/SuppressedFinalizerDisposable.cs index 4a5c6d12..96fa21cb 100644 --- a/Common/Classes/Disposables/SuppressedFinalizerDisposable.cs +++ b/Common/Classes/Disposables/SuppressedFinalizerDisposable.cs @@ -55,7 +55,7 @@ public SuppressedFinalizerDisposable(bool shouldDispose) internal void Resurrect(ref int managedThreadId) { //Need to retrieve the state from this instance. - long state = 0; + int state = 0; //Not already disposed or destructing? if (IsUndisposed is false | BaseDisposable.IsDestructing(this, ref state) is false) return; From f590b3e4eda6f7561e817cf973bfc9c1cac26c15 Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Thu, 14 Dec 2023 23:08:35 +0300 Subject: [PATCH 05/10] rtpclient: Busy wait for 100 cycles creates too much CPU load --- Rtp/RtpClient.Fields.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Rtp/RtpClient.Fields.cs b/Rtp/RtpClient.Fields.cs index 74a49022..1b7656de 100644 --- a/Rtp/RtpClient.Fields.cs +++ b/Rtp/RtpClient.Fields.cs @@ -62,7 +62,7 @@ public partial class RtpClient private readonly Media.Common.Collections.Generic.ConcurrentLinkedQueueSlim<(RtpClient.TransportContext Context, Common.BaseDisposable Packet, bool Final, bool Received)> m_EventData = new(); //Todo, LinkedQueue and Clock. - private readonly System.Threading.ManualResetEventSlim m_EventReady = new(false, 100); //should be caluclated based on memory and speed. SpinWait uses 10 as a default. + private readonly System.Threading.ManualResetEventSlim m_EventReady = new(false, spinCount: 20); //should be caluclated based on memory and speed. SpinWait uses 10 as a default. //Outgoing Packets, Not a Queue because you cant re-order a Queue (in place) and you can't take a range from the Queue (in a single operation) //Those things aside, ordering is not performed here and only single packets are iterated and would eliminate the need for removing after the operation. From 38fe9623b84f865aef7e1407fb8f984dd54e1ee9 Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Sat, 16 Dec 2023 15:51:02 +0300 Subject: [PATCH 06/10] rtpsserver: Better maintenance timing control from API side --- RtspServer/RtspServer.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/RtspServer/RtspServer.cs b/RtspServer/RtspServer.cs index dc699058..f4dc970e 100644 --- a/RtspServer/RtspServer.cs +++ b/RtspServer/RtspServer.cs @@ -65,6 +65,12 @@ public class RtspServer : Common.BaseDisposable, Common.ISocketReference, Common //Milliseconds internal const int DefaultSendTimeout = 500; + // Minimum timout to use for RTSP clients. + private readonly TimeSpan MinimumRtspClientInactivityTimeout = TimeSpan.FromSeconds(1); + + // Default timout to use for RTSP clients. + private readonly TimeSpan DefaultRtspClientInactivityTimeout = TimeSpan.FromSeconds(60); + internal static void ConfigureRtspServerThread(Thread thread) { thread.TrySetApartmentState(ApartmentState.MTA); @@ -673,7 +679,9 @@ public RtspServer(IPAddress listenAddress, int listenPort, TimeSpan? inactivityT public RtspServer(IPEndPoint listenEndPoint, TimeSpan? inactivityTimeout = null, bool shouldDispose = true) : base(shouldDispose) { - RtspClientInactivityTimeout = inactivityTimeout ?? TimeSpan.FromSeconds(60); + RtspClientInactivityTimeout = inactivityTimeout.HasValue + ? Media.Common.Extensions.TimeSpan.TimeSpanExtensions.Max(inactivityTimeout.Value, MinimumRtspClientInactivityTimeout) + : DefaultRtspClientInactivityTimeout; //Check syntax of Server header to ensure allowed format... ServerName = "ASTI Media Server RTSP\\" + Version.ToString(RtspMessage.VersionFormat, System.Globalization.CultureInfo.InvariantCulture); @@ -1283,7 +1291,7 @@ public virtual async Task StartAsync(bool allowAddressReuse = false, bool allowP //Timer for maintaince (one quarter of the ticks) m_Maintainer = new Timer(MaintainServer, null, - TimeSpan.FromTicks(RtspClientInactivityTimeout.Ticks >> 2), + TimeSpan.FromTicks(Math.Max(RtspClientInactivityTimeout.Ticks >> 4, MinimumRtspClientInactivityTimeout.Ticks)), Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan); if (m_UdpPort >= 0) EnableUnreliableTransport(m_UdpPort); From d765e08ed5035e055a5c500c18fe82a0b4271bd3 Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Sat, 16 Dec 2023 15:59:59 +0300 Subject: [PATCH 07/10] rtspserver: High-speed queue emptiness check --- Common/Collections/Generic/ConcurrentLinkedQueueSlim.cs | 6 +++--- RtspServer/MediaTypes/RFC2435Media.cs | 2 +- RtspServer/MediaTypes/RtpAudioSink.cs | 2 +- RtspServer/MediaTypes/RtpSink.cs | 2 +- RtspServer/MediaTypes/RtpVideoSink.cs | 2 +- 5 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Common/Collections/Generic/ConcurrentLinkedQueueSlim.cs b/Common/Collections/Generic/ConcurrentLinkedQueueSlim.cs index 3ac8f766..edd4c3eb 100644 --- a/Common/Collections/Generic/ConcurrentLinkedQueueSlim.cs +++ b/Common/Collections/Generic/ConcurrentLinkedQueueSlim.cs @@ -41,6 +41,7 @@ The above copyright notice and this permission notice shall be included in all c using System; using System.Collections.Generic; using System.Linq; +using System.Threading; #endregion @@ -217,7 +218,7 @@ public long Count public bool IsEmpty { [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] - get { return Count is Common.Binary.LongZero; } + get { return Volatile.Read(ref First) is null; } } #endregion @@ -324,8 +325,7 @@ public bool TryPeek(ref T t) [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] public void Enqueue(T t) { - bool added = false; - + bool added; do added = TryEnqueue(ref t); while (added is false); } diff --git a/RtspServer/MediaTypes/RFC2435Media.cs b/RtspServer/MediaTypes/RFC2435Media.cs index bc4f8fa4..d23706c8 100644 --- a/RtspServer/MediaTypes/RFC2435Media.cs +++ b/RtspServer/MediaTypes/RFC2435Media.cs @@ -2527,7 +2527,7 @@ internal override void SendPackets() { try { - if (Frames.Count is 0 && State == StreamState.Started) + if (Frames.IsEmpty && State == StreamState.Started) { if (RtpClient.IsActive) RtpClient.m_WorkerThread.Priority = System.Threading.ThreadPriority.Lowest; diff --git a/RtspServer/MediaTypes/RtpAudioSink.cs b/RtspServer/MediaTypes/RtpAudioSink.cs index 247e4e7f..a98ffb22 100644 --- a/RtspServer/MediaTypes/RtpAudioSink.cs +++ b/RtspServer/MediaTypes/RtpAudioSink.cs @@ -68,7 +68,7 @@ internal override void SendPackets() { try { - if (Frames.Count is 0 && State == StreamState.Started) + if (Frames.IsEmpty && State == StreamState.Started) { if (RtpClient.IsActive) RtpClient.m_WorkerThread.Priority = System.Threading.ThreadPriority.Lowest; diff --git a/RtspServer/MediaTypes/RtpSink.cs b/RtspServer/MediaTypes/RtpSink.cs index cb9c31b9..54c3fa0b 100644 --- a/RtspServer/MediaTypes/RtpSink.cs +++ b/RtspServer/MediaTypes/RtpSink.cs @@ -155,7 +155,7 @@ internal virtual void SendPackets() { try { - if (Packets.Count is 0) + if (Packets.IsEmpty) { System.Threading.Thread.Sleep(0); diff --git a/RtspServer/MediaTypes/RtpVideoSink.cs b/RtspServer/MediaTypes/RtpVideoSink.cs index d5c1c45a..bb27e6d2 100644 --- a/RtspServer/MediaTypes/RtpVideoSink.cs +++ b/RtspServer/MediaTypes/RtpVideoSink.cs @@ -188,7 +188,7 @@ internal override void SendPackets() { try { - if (Frames.Count is 0 && State is StreamState.Started) + if (Frames.IsEmpty && State is StreamState.Started) { if (RtpClient.IsActive) RtpClient.m_WorkerThread.Priority = ThreadPriority.Lowest; From e3379a73ae8f02ab73ebafde9b4ffcd281a755ae Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Sat, 16 Dec 2023 16:12:56 +0300 Subject: [PATCH 08/10] rtspserver: One microsecond wait time creates too much CPU load. No streams with 1000000 * N frames known --- Rtp/RtpClient.Fields.cs | 3 +++ Rtp/RtpClient.Methods.cs | 2 +- Rtp/RtpClient.cs | 13 +++++++++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/Rtp/RtpClient.Fields.cs b/Rtp/RtpClient.Fields.cs index 1b7656de..050973b5 100644 --- a/Rtp/RtpClient.Fields.cs +++ b/Rtp/RtpClient.Fields.cs @@ -56,6 +56,9 @@ public partial class RtpClient internal bool m_StopRequested, m_ThreadEvents, //on or off right now, int could allow levels of threading.. m_IListSockets; //Indicates if to use the IList send overloads. + // How much time to wait between event queue checks. + private System.TimeSpan m_WaitIntervalBetweenEvents = Media.Common.Extensions.TimeSpan.TimeSpanExtensions.OneMillisecond; + //Collection to handle the dispatch of events. //Notes that Collections.Concurrent.Queue may be better suited for this in production until the ConcurrentLinkedQueue has been thoroughly engineered and tested. //The context, the item, final, recieved diff --git a/Rtp/RtpClient.Methods.cs b/Rtp/RtpClient.Methods.cs index 5b7a4a05..5524390a 100644 --- a/Rtp/RtpClient.Methods.cs +++ b/Rtp/RtpClient.Methods.cs @@ -2256,7 +2256,7 @@ private void HandleEvents() m_EventReady.Reset(); while (IsActive && m_EventData.IsEmpty) - m_EventReady.Wait(Common.Extensions.TimeSpan.TimeSpanExtensions.OneMicrosecond); + m_EventReady.Wait(WaitIntervalBetweenEvents); } else if (IsActive is false) break; diff --git a/Rtp/RtpClient.cs b/Rtp/RtpClient.cs index a7c90757..5d457d5c 100644 --- a/Rtp/RtpClient.cs +++ b/Rtp/RtpClient.cs @@ -3912,6 +3912,19 @@ public bool IListSockets } } + /// + /// How much time to wait between event queue checks. + /// High values reduce event handling speed / FPS, but also reduce CPU consumption. + /// + public TimeSpan WaitIntervalBetweenEvents + { + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized | System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + get => m_WaitIntervalBetweenEvents; + + [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized | System.Runtime.CompilerServices.MethodImplOptions.AggressiveInlining)] + set => m_WaitIntervalBetweenEvents = value; + } + /// /// Gets or sets a value which indicates if events will be threaded or not. /// If threading is enabled the call will block until the event thread has started. From 945933fe3a9aa352a71ae96b7ecfdf5ba8df63b9 Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Sat, 16 Dec 2023 16:14:27 +0300 Subject: [PATCH 09/10] all: Do not lose exception stacktraces in logs Hard to debug issues when no stacktrace. --- Http/HttpClient.cs | 1 + Rtp/RtpClient.Methods.cs | 9 ++++++++- Rtsp/RtspClient.cs | 34 ++++++++++++++++++++++++++++------ Rtsp/RtspSession.cs | 4 +++- 4 files changed, 40 insertions(+), 8 deletions(-) diff --git a/Http/HttpClient.cs b/Http/HttpClient.cs index 758b2502..159c77ef 100644 --- a/Http/HttpClient.cs +++ b/Http/HttpClient.cs @@ -1299,6 +1299,7 @@ m_LastTransmitted is not null && message is not null && catch (Exception ex) { Common.ILoggingExtensions.Log(Logger, ToString() + "@SendHttpMessage: " + ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); } finally { diff --git a/Rtp/RtpClient.Methods.cs b/Rtp/RtpClient.Methods.cs index 5524390a..10132f88 100644 --- a/Rtp/RtpClient.Methods.cs +++ b/Rtp/RtpClient.Methods.cs @@ -2267,7 +2267,13 @@ private void HandleEvents() HandleEvent(); } } - catch (System.Exception ex) { Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@HandleEvents: " + ex.Message); goto Begin; } + catch (System.Exception ex) + { + Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@HandleEvents: " + ex.Message); + Media.Common.ILoggingExtensions.LogException(Logger, ex); + + goto Begin; + } } } @@ -2777,6 +2783,7 @@ private void SendReceieve() catch (System.Exception ex) { Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@SendRecieve: " + ex.Message); + Media.Common.ILoggingExtensions.LogException(Logger, ex); if (critical) System.Threading.Thread.EndCriticalRegion(); diff --git a/Rtsp/RtspClient.cs b/Rtsp/RtspClient.cs index e803f2b7..078ba3b8 100644 --- a/Rtsp/RtspClient.cs +++ b/Rtsp/RtspClient.cs @@ -1019,6 +1019,7 @@ public bool IsPlaying catch (Exception ex) { Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@IsPlaying - " + ex.Message); + Media.Common.ILoggingExtensions.LogException(Logger, ex); } } @@ -3083,7 +3084,7 @@ public void StopPlaying(IEnumerable mediaDescriptions, bool fo public void StopPlaying(bool disconnectSocket = true) { try { Disconnect(disconnectSocket); } - catch (Exception ex) { Media.Common.ILoggingExtensions.Log(Logger, ex.Message); } + catch (Exception ex) { Media.Common.ILoggingExtensions.LogException(Logger, ex); } } [System.Runtime.CompilerServices.MethodImpl(System.Runtime.CompilerServices.MethodImplOptions.Synchronized)] @@ -3318,7 +3319,7 @@ message header. } catch (Exception ex) { - Common.ILoggingExtensions.Log(Logger, ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); throw; } @@ -4623,6 +4624,7 @@ SharesSocket is false && catch (Exception ex) { Common.ILoggingExtensions.Log(Logger, ToString() + "@SendRtspMessage: " + ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); } finally { @@ -5967,7 +5969,11 @@ InUse is false && DisableKeepAliveRequest = keepAlives; } } - catch (Exception ex) { Common.ILoggingExtensions.Log(Logger, ToString() + "@MonitorProtocol: " + ex.Message); } + catch (Exception ex) + { + Common.ILoggingExtensions.Log(Logger, ToString() + "@MonitorProtocol: " + ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); + } //If not disposed AND IsConnected and if protocol switch is still allowed AND IsPlaying and not already TCP if (Common.IDisposedExtensions.IsNullOrDisposed(this) is false && @@ -6047,6 +6053,7 @@ tc.TotalPacketsSent is Common.Binary.LongZero && catch (Exception ex) { Common.ILoggingExtensions.Log(Logger, ToString() + "@MonitorProtocol: " + ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); } } } @@ -6070,8 +6077,19 @@ tc.TotalPacketsSent is Common.Binary.LongZero && //If there is still a timer change it based on the last messages round trip time, should be relative to all messages... if (Common.IDisposedExtensions.IsNullOrDisposed(this) is false && m_ProtocolMonitor is not null) - try { m_ProtocolMonitor.Change(m_ConnectionTime.Add(LastMessageRoundTripTime), Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan); } - catch (Exception ex) { Common.ILoggingExtensions.Log(Logger, ToString() + "@MonitorProtocol: " + ex.Message); } + try + { + m_ProtocolMonitor.Change + ( + m_ConnectionTime.Add(LastMessageRoundTripTime), + Media.Common.Extensions.TimeSpan.TimeSpanExtensions.InfiniteTimeSpan + ); + } + catch (Exception ex) + { + Common.ILoggingExtensions.Log(Logger, ToString() + "@MonitorProtocol: " + ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); + } } public RtspMessage SendPlay(MediaDescription mediaDescription, TimeSpan? startTime = null, TimeSpan? endTime = null, string rangeType = "npt") @@ -6514,7 +6532,11 @@ context.Goodbye is null || } } - catch (Exception ex) { Common.ILoggingExtensions.Log(Logger, ToString() + "@SendKeepAlive: " + ex.Message); } + catch (Exception ex) + { + Common.ILoggingExtensions.Log(Logger, ToString() + "@SendKeepAlive: " + ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); + } //Raise the stopping event if not playing anymore //if (true == wasPlaying && false == IsPlaying) OnStopping(); diff --git a/Rtsp/RtspSession.cs b/Rtsp/RtspSession.cs index 91d1d3ee..38694696 100644 --- a/Rtsp/RtspSession.cs +++ b/Rtsp/RtspSession.cs @@ -752,6 +752,7 @@ public bool IsPlaying catch (Exception ex) { Media.Common.ILoggingExtensions.Log(Logger, ToString() + "@IsPlaying - " + ex.Message); + Media.Common.ILoggingExtensions.LogException(Logger, ex); } } @@ -1702,6 +1703,7 @@ SharesSocket is false && catch (Exception ex) { Common.ILoggingExtensions.Log(Logger, ToString() + "@SendRtspMessage: " + ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); } finally { @@ -2399,7 +2401,7 @@ message header. } catch (Exception ex) { - Common.ILoggingExtensions.Log(Logger, ex.Message); + Common.ILoggingExtensions.LogException(Logger, ex); throw; } From 02be50ac59cb1f86bce0d5ef8a883bb3d3fe783c Mon Sep 17 00:00:00 2001 From: Dmitry Tsarevich Date: Sat, 16 Dec 2023 16:28:17 +0300 Subject: [PATCH 10/10] tests: Using block already do Dispose automatically --- UnitTests/Program.cs | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/UnitTests/Program.cs b/UnitTests/Program.cs index de172a6c..9b92861d 100644 --- a/UnitTests/Program.cs +++ b/UnitTests/Program.cs @@ -144,15 +144,15 @@ public class Program public static async Task Main(string[] args) { //Run the main tests - foreach (Action test in LogicTests) RunTest(test); + //foreach (Action test in LogicTests) RunTest(test); - Console.WriteLine("Logic Tests Complete! Press Q to Exit or any other key to perform the live tests."); + //Console.WriteLine("Logic Tests Complete! Press Q to Exit or any other key to perform the live tests."); - if (Console.ReadKey(true).Key == ConsoleKey.Q) return; + //if (Console.ReadKey(true).Key == ConsoleKey.Q) return; - RunTest(HttpClientTests); + //RunTest(HttpClientTests); - RunTest(RtspClientTests); + //RunTest(RtspClientTests); await RunTestAsync(TestServerAsync, nameof(TestServerAsync)).ConfigureAwait(false); } @@ -2471,10 +2471,6 @@ private static async Task TestServerAsync() { server.Logger.LogException(ex); - gfxScreenshot.Dispose(); - - bmpScreenshot.Dispose(); - if (server is not null && server.IsRunning) goto Start; } }