From ff77cbbe1d188b4a274ec072fbba7770aab2cceb Mon Sep 17 00:00:00 2001 From: danielgrabowski Date: Tue, 5 Nov 2019 10:30:31 +0100 Subject: [PATCH 1/3] Fix memory leaks while streaming --- Squirrowse.Client/Service/Camera.cs | 2 +- Squirrowse.Client/Service/CameraService.cs | 15 ++++++--------- Squirrowse.Client/Service/StreamService.cs | 10 ++++------ Squirrowse.Client/Worker.cs | 11 +++++++---- Squirrowse.Core/Services/ImgExtensions.cs | 2 +- Squirrowse.Service/Hubs/StreamHub.cs | 12 +++++++++--- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/Squirrowse.Client/Service/Camera.cs b/Squirrowse.Client/Service/Camera.cs index 1a83f47..54e1946 100644 --- a/Squirrowse.Client/Service/Camera.cs +++ b/Squirrowse.Client/Service/Camera.cs @@ -9,7 +9,7 @@ namespace Squirrowse.Client.Service private readonly int Height; private readonly int Width; - public Camera(int height = 480, int width = 640, double fps = 15f) + public Camera(int height = 480, int width = 640, double fps = 30f) { Height = height; Width = width; diff --git a/Squirrowse.Client/Service/CameraService.cs b/Squirrowse.Client/Service/CameraService.cs index 685355d..3a3d148 100644 --- a/Squirrowse.Client/Service/CameraService.cs +++ b/Squirrowse.Client/Service/CameraService.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Runtime.CompilerServices; +using System.Collections.Generic; using System.Threading.Tasks; using OpenCvSharp; using Squirrowse.Core.Services; @@ -10,6 +8,7 @@ namespace Squirrowse.Client.Service public class CameraService : ICameraService { private readonly VideoCapture _videoCapture; + public CameraService(CameraFactory cam) { _videoCapture = cam.GetCamera(); @@ -17,18 +16,16 @@ namespace Squirrowse.Client.Service public async Task GetFrame() - { + { var video = _videoCapture.RetrieveMat(); return video; } public async IAsyncEnumerable GetFramesAsyncEnumerator() { - while (true) - { - var fr = await GetFrame(); - yield return fr.ConvertToJpgByte(); - } + using var fr = await GetFrame(); + yield return fr.ConvertToJpgByte(); + //fr.Dispose(); } } } \ No newline at end of file diff --git a/Squirrowse.Client/Service/StreamService.cs b/Squirrowse.Client/Service/StreamService.cs index a8652b3..f05d66c 100644 --- a/Squirrowse.Client/Service/StreamService.cs +++ b/Squirrowse.Client/Service/StreamService.cs @@ -27,12 +27,10 @@ namespace Squirrowse.Client.Service public async Task SendStreamAsync(IAsyncEnumerable asb) { - while (true) - { - - logger.LogInformation($"{nameof(SendStreamAsync)} Start stream"); - await session.SendAsync("UploadByteStream", asb); - } + logger.LogInformation($"{nameof(SendStreamAsync)} Start stream"); + await session.SendAsync("UploadByteStream", asb); + + logger.LogInformation($"{nameof(SendStreamAsync)} End stream"); } } } \ No newline at end of file diff --git a/Squirrowse.Client/Worker.cs b/Squirrowse.Client/Worker.cs index bf7ab9e..8bcc13f 100644 --- a/Squirrowse.Client/Worker.cs +++ b/Squirrowse.Client/Worker.cs @@ -3,6 +3,7 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; +using OpenCvSharp; using Squirrowse.Client.Service; namespace Squirrowse.Client @@ -22,14 +23,16 @@ namespace Squirrowse.Client protected override async Task ExecuteAsync(CancellationToken stoppingToken) { - + + while (!stoppingToken.IsCancellationRequested) + { logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); await streamService.SendStreamAsync(camera.GetFramesAsyncEnumerator()); - - // await Task.Delay(1000, stoppingToken); - + + // await Task.Delay(50, stoppingToken); + } } } } \ No newline at end of file diff --git a/Squirrowse.Core/Services/ImgExtensions.cs b/Squirrowse.Core/Services/ImgExtensions.cs index 26382fa..bcca8d5 100644 --- a/Squirrowse.Core/Services/ImgExtensions.cs +++ b/Squirrowse.Core/Services/ImgExtensions.cs @@ -13,7 +13,7 @@ namespace Squirrowse.Core.Services public static Mat ConvertByteToMat(this byte[] bytearr) { - using var tempMat = Cv2.ImDecode(bytearr, ImreadModes.Unchanged); //keep as disposable + var tempMat = Cv2.ImDecode(bytearr, ImreadModes.Unchanged); //keep as disposable return tempMat ?? new Mat(); } } diff --git a/Squirrowse.Service/Hubs/StreamHub.cs b/Squirrowse.Service/Hubs/StreamHub.cs index 9a18994..a2f7fbe 100644 --- a/Squirrowse.Service/Hubs/StreamHub.cs +++ b/Squirrowse.Service/Hubs/StreamHub.cs @@ -3,7 +3,9 @@ using System.Collections.Generic; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR; using Microsoft.Extensions.Logging; +using OpenCvSharp; using Squirrowse.Core.Models; +using Squirrowse.Core.Services; namespace Squirrowse.Service.Hubs { @@ -59,13 +61,17 @@ namespace Squirrowse.Service.Hubs await client.SendAsync("Stop"); } - + public Window okno = new Window("test"); public async Task UploadByteStream(IAsyncEnumerable stream) { + await foreach (var frame in stream) { - logger.LogInformation($"Got frame size: {frame.Length} "); - await Task.Delay(100); //leave some delay for debug purpose + using var imgbuffer = frame.ConvertByteToMat(); + okno.ShowImage(imgbuffer); + Cv2.WaitKey(1); + // logger.LogInformation($"Got frame size: {frame.Length} "); + // await Task.Delay(100); //leave some delay for debug purpose } } } From 2e624d9b7a01bb930d07b9b0a3b938b8dba10392 Mon Sep 17 00:00:00 2001 From: danielgrabowski Date: Tue, 5 Nov 2019 17:00:36 +0100 Subject: [PATCH 2/3] Action dispatcher --- Squirrowse.Client/Program.cs | 2 +- .../{StreamService.cs => ActionDispatcher.cs} | 22 ++++++++---- .../Service/ConnectionManager.cs | 21 +++++++++-- ...IStreamService.cs => IActionDispatcher.cs} | 4 ++- .../Service/IConnectionManager.cs | 5 +-- Squirrowse.Client/Worker.cs | 13 +++---- Squirrowse.Core/Models/User.cs | 14 ++++++++ Squirrowse.Service/Hubs/IStreamHub.cs | 2 +- Squirrowse.Service/Hubs/IStreamManager.cs | 16 +++++++++ Squirrowse.Service/Hubs/StreamHub.cs | 9 ++--- Squirrowse.Service/Hubs/StreamManager.cs | 35 +++++++++++++++++++ Squirrowse.Service/IUserProvider.cs | 7 ---- Squirrowse.Service/UserProvider.cs | 12 ------- 13 files changed, 120 insertions(+), 42 deletions(-) rename Squirrowse.Client/Service/{StreamService.cs => ActionDispatcher.cs} (62%) rename Squirrowse.Client/Service/{IStreamService.cs => IActionDispatcher.cs} (68%) create mode 100644 Squirrowse.Core/Models/User.cs create mode 100644 Squirrowse.Service/Hubs/IStreamManager.cs create mode 100644 Squirrowse.Service/Hubs/StreamManager.cs delete mode 100644 Squirrowse.Service/IUserProvider.cs delete mode 100644 Squirrowse.Service/UserProvider.cs diff --git a/Squirrowse.Client/Program.cs b/Squirrowse.Client/Program.cs index 0cad8cd..661b03a 100644 --- a/Squirrowse.Client/Program.cs +++ b/Squirrowse.Client/Program.cs @@ -19,7 +19,7 @@ namespace Squirrowse.Client services.AddHostedService(); services.AddSingleton(x=> new ConnectionManager("http://localhost", 5000)); //keep as transient for now services.AddSingleton< ICameraService, CameraService >(x=>new CameraService(new Camera())); - services.AddTransient(); + services.AddTransient(); }); } diff --git a/Squirrowse.Client/Service/StreamService.cs b/Squirrowse.Client/Service/ActionDispatcher.cs similarity index 62% rename from Squirrowse.Client/Service/StreamService.cs rename to Squirrowse.Client/Service/ActionDispatcher.cs index f05d66c..dd4da7f 100644 --- a/Squirrowse.Client/Service/StreamService.cs +++ b/Squirrowse.Client/Service/ActionDispatcher.cs @@ -6,18 +6,23 @@ using Squirrowse.Core.Models; namespace Squirrowse.Client.Service { - public class StreamService : IStreamService + public class ActionDispatcher : IActionDispatcher { private readonly IConnectionManager connectionManager; - private readonly ILogger logger; + private readonly ILogger logger; private readonly HubConnection session; - - public StreamService(ILogger logger, IConnectionManager connectionManager) + + public ActionDispatcher(ILogger logger, IConnectionManager connectionManager) { this.connectionManager = connectionManager; this.logger = logger; - session = connectionManager.Connect(); - session.StartAsync(); + session.On("Start",StartStream); + session.On("Stop",StopStream); + } + + public Task StopStream() + { + throw new System.NotImplementedException(); } public async Task SayHello() @@ -32,5 +37,10 @@ namespace Squirrowse.Client.Service logger.LogInformation($"{nameof(SendStreamAsync)} End stream"); } + + public Task StartStream() + { + throw new System.NotImplementedException(); + } } } \ No newline at end of file diff --git a/Squirrowse.Client/Service/ConnectionManager.cs b/Squirrowse.Client/Service/ConnectionManager.cs index 28c8f54..60dc8b1 100644 --- a/Squirrowse.Client/Service/ConnectionManager.cs +++ b/Squirrowse.Client/Service/ConnectionManager.cs @@ -1,4 +1,6 @@ -using Microsoft.AspNetCore.SignalR.Client; +using System; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Client; using Microsoft.Extensions.DependencyInjection; namespace Squirrowse.Client.Service @@ -6,6 +8,7 @@ namespace Squirrowse.Client.Service public class ConnectionManager : IConnectionManager { private readonly HubConnection _connection; + private bool Connected; public ConnectionManager(string url, int port) { @@ -14,11 +17,25 @@ namespace Squirrowse.Client.Service .AddMessagePackProtocol() .WithAutomaticReconnect() .Build(); + } - public HubConnection Connect() + public async Task GetConnection() { + if (Connected) + { + return _connection; + } + + await _connection.StartAsync(); + await RegisterOnHub(); + Connected = true; return _connection; } + + private async Task RegisterOnHub() + { + await _connection.SendAsync("AddUser", Environment.UserName); + } } } \ No newline at end of file diff --git a/Squirrowse.Client/Service/IStreamService.cs b/Squirrowse.Client/Service/IActionDispatcher.cs similarity index 68% rename from Squirrowse.Client/Service/IStreamService.cs rename to Squirrowse.Client/Service/IActionDispatcher.cs index 8dbe9f5..cca49fc 100644 --- a/Squirrowse.Client/Service/IStreamService.cs +++ b/Squirrowse.Client/Service/IActionDispatcher.cs @@ -3,9 +3,11 @@ using System.Threading.Tasks; namespace Squirrowse.Client.Service { - public interface IStreamService + public interface IActionDispatcher { Task SendStreamAsync(IAsyncEnumerable asb); + Task StartStream(); + Task StopStream(); Task SayHello(); } } \ No newline at end of file diff --git a/Squirrowse.Client/Service/IConnectionManager.cs b/Squirrowse.Client/Service/IConnectionManager.cs index c530417..4edf008 100644 --- a/Squirrowse.Client/Service/IConnectionManager.cs +++ b/Squirrowse.Client/Service/IConnectionManager.cs @@ -1,9 +1,10 @@ -using Microsoft.AspNetCore.SignalR.Client; +using System.Threading.Tasks; +using Microsoft.AspNetCore.SignalR.Client; namespace Squirrowse.Client.Service { public interface IConnectionManager { - HubConnection Connect(); + Task GetConnection(); } } \ No newline at end of file diff --git a/Squirrowse.Client/Worker.cs b/Squirrowse.Client/Worker.cs index 8bcc13f..68bb130 100644 --- a/Squirrowse.Client/Worker.cs +++ b/Squirrowse.Client/Worker.cs @@ -1,4 +1,5 @@ using System; +using System.Drawing; using System.Threading; using System.Threading.Tasks; using Microsoft.Extensions.Hosting; @@ -11,13 +12,13 @@ namespace Squirrowse.Client public class Worker : BackgroundService { private readonly ILogger logger; - private readonly IStreamService streamService; + private readonly IConnectionManager _connectionManager; private readonly ICameraService camera; - public Worker(ILogger logger, IStreamService streamService, ICameraService camera) + public Worker(ILogger logger, IConnectionManager connectionManager, ICameraService camera) { this.logger = logger; - this.streamService = streamService; + this._connectionManager = connectionManager; this.camera = camera; } @@ -28,10 +29,10 @@ namespace Squirrowse.Client { logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); + await _connectionManager.GetConnection(); + //await _connectionManager.SendStreamAsync(camera.GetFramesAsyncEnumerator()); - await streamService.SendStreamAsync(camera.GetFramesAsyncEnumerator()); - - // await Task.Delay(50, stoppingToken); + // await Task.Delay(50, stoppingToken); } } } diff --git a/Squirrowse.Core/Models/User.cs b/Squirrowse.Core/Models/User.cs new file mode 100644 index 0000000..4af3ea6 --- /dev/null +++ b/Squirrowse.Core/Models/User.cs @@ -0,0 +1,14 @@ +namespace Squirrowse.Core.Models +{ + public class User + { + public string ConnectionId { get; set; } + public string AgentName { get; set; } + + public User(string connectionId, string agentName) + { + ConnectionId = connectionId; + AgentName = agentName; + } + } +} \ No newline at end of file diff --git a/Squirrowse.Service/Hubs/IStreamHub.cs b/Squirrowse.Service/Hubs/IStreamHub.cs index 32e0a0a..77cb798 100644 --- a/Squirrowse.Service/Hubs/IStreamHub.cs +++ b/Squirrowse.Service/Hubs/IStreamHub.cs @@ -6,7 +6,7 @@ namespace Squirrowse.Service.Hubs { public interface IStreamHub { - Task AddClient(); + Task AddUser(string username); Task UploadByteStream(IAsyncEnumerable stream); Task Startstream(string userId); Task StopStream(string userId); diff --git a/Squirrowse.Service/Hubs/IStreamManager.cs b/Squirrowse.Service/Hubs/IStreamManager.cs new file mode 100644 index 0000000..41dc26f --- /dev/null +++ b/Squirrowse.Service/Hubs/IStreamManager.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Squirrowse.Service.Hubs +{ + public interface IStreamManager + { + Task AddUser(string connectionId, string agentName); + Task RemoveUserbyConnectionId(string connectionId); + Task RemoveUserByUserName(string agentName); + bool CheckUser(string agentName); + + } +} diff --git a/Squirrowse.Service/Hubs/StreamHub.cs b/Squirrowse.Service/Hubs/StreamHub.cs index a2f7fbe..d9d8113 100644 --- a/Squirrowse.Service/Hubs/StreamHub.cs +++ b/Squirrowse.Service/Hubs/StreamHub.cs @@ -12,15 +12,16 @@ namespace Squirrowse.Service.Hubs public class StreamHub : Hub, IStreamHub //fujka { private readonly ILogger logger; - - public StreamHub(ILogger logger) + private readonly IStreamManager manager; + public StreamHub(ILogger logger, IStreamManager manager) { this.logger = logger; + this.manager = manager; } - public async Task AddClient() + public async Task AddUser(string UserName) { - throw new NotImplementedException(); + await manager.AddUser(Context.ConnectionId, UserName); } public async Task AddToGroup(Groups group, string user = "") diff --git a/Squirrowse.Service/Hubs/StreamManager.cs b/Squirrowse.Service/Hubs/StreamManager.cs new file mode 100644 index 0000000..d2a3c6e --- /dev/null +++ b/Squirrowse.Service/Hubs/StreamManager.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Squirrowse.Core.Models; + +namespace Squirrowse.Service.Hubs +{ + public class StreamManager : IStreamManager + { + private readonly List _users = new List(); //temporary + + public Task AddUser(string connectionId, string userName) + { + _users.Add(new User(connectionId, userName)); + return Task.CompletedTask; + } + + public Task RemoveUserbyConnectionId(string connectionId) + { + _users.Remove(_users.First(user => user.ConnectionId == connectionId)); + return Task.CompletedTask; + } + + public Task RemoveUserByUserName(string userName) + { + _users.RemoveAll(user => user.AgentName == userName); + return Task.CompletedTask; + } + + public bool CheckUser(string userName) + { + return _users.Any(user => user.AgentName == userName); + } + } +} \ No newline at end of file diff --git a/Squirrowse.Service/IUserProvider.cs b/Squirrowse.Service/IUserProvider.cs deleted file mode 100644 index 64425be..0000000 --- a/Squirrowse.Service/IUserProvider.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Squirrowse.Service -{ - public interface IUserProvider - { - string GetUserId(); - } -} \ No newline at end of file diff --git a/Squirrowse.Service/UserProvider.cs b/Squirrowse.Service/UserProvider.cs deleted file mode 100644 index eb1d2a3..0000000 --- a/Squirrowse.Service/UserProvider.cs +++ /dev/null @@ -1,12 +0,0 @@ -using System; - -namespace Squirrowse.Service -{ - public class UserProvider : IUserProvider - { - public string GetUserId() - { - throw new NotImplementedException(); - } - } -} \ No newline at end of file From f444c9fbae8f292377fa1b24ebf1f87d99840425 Mon Sep 17 00:00:00 2001 From: danielgrabowski Date: Wed, 6 Nov 2019 11:49:39 +0100 Subject: [PATCH 3/3] Refactor hub/client --- Squirrowse.Client/Program.cs | 2 +- .../Service/ConnectionManager.cs | 19 +++++++++++++++++-- .../Service/IConnectionManager.cs | 2 ++ Squirrowse.Client/Worker.cs | 18 +++++++----------- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/Squirrowse.Client/Program.cs b/Squirrowse.Client/Program.cs index 661b03a..3af0c5b 100644 --- a/Squirrowse.Client/Program.cs +++ b/Squirrowse.Client/Program.cs @@ -19,7 +19,7 @@ namespace Squirrowse.Client services.AddHostedService(); services.AddSingleton(x=> new ConnectionManager("http://localhost", 5000)); //keep as transient for now services.AddSingleton< ICameraService, CameraService >(x=>new CameraService(new Camera())); - services.AddTransient(); + services.AddSingleton(); }); } diff --git a/Squirrowse.Client/Service/ConnectionManager.cs b/Squirrowse.Client/Service/ConnectionManager.cs index 60dc8b1..642c289 100644 --- a/Squirrowse.Client/Service/ConnectionManager.cs +++ b/Squirrowse.Client/Service/ConnectionManager.cs @@ -26,11 +26,26 @@ namespace Squirrowse.Client.Service { return _connection; } - + + throw new Exception(); + } + + public async Task InitConnection() + { + if (_connection.State == HubConnectionState.Connected) + { + return; + } await _connection.StartAsync(); await RegisterOnHub(); Connected = true; - return _connection; + } + + public async Task Disconnect() + { + if (_connection.State == HubConnectionState.Disconnected) throw new Exception(); + await _connection.StopAsync(); + Connected = false; } private async Task RegisterOnHub() diff --git a/Squirrowse.Client/Service/IConnectionManager.cs b/Squirrowse.Client/Service/IConnectionManager.cs index 4edf008..5398613 100644 --- a/Squirrowse.Client/Service/IConnectionManager.cs +++ b/Squirrowse.Client/Service/IConnectionManager.cs @@ -6,5 +6,7 @@ namespace Squirrowse.Client.Service public interface IConnectionManager { Task GetConnection(); + Task InitConnection(); + Task Disconnect(); } } \ No newline at end of file diff --git a/Squirrowse.Client/Worker.cs b/Squirrowse.Client/Worker.cs index 68bb130..7c714e4 100644 --- a/Squirrowse.Client/Worker.cs +++ b/Squirrowse.Client/Worker.cs @@ -9,7 +9,7 @@ using Squirrowse.Client.Service; namespace Squirrowse.Client { - public class Worker : BackgroundService + public class Worker : IHostedService { private readonly ILogger logger; private readonly IConnectionManager _connectionManager; @@ -22,18 +22,14 @@ namespace Squirrowse.Client this.camera = camera; } - protected override async Task ExecuteAsync(CancellationToken stoppingToken) + public async Task StartAsync(CancellationToken cancellationToken) { + await _connectionManager.InitConnection(); + } - while (!stoppingToken.IsCancellationRequested) - { - logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now); - - await _connectionManager.GetConnection(); - //await _connectionManager.SendStreamAsync(camera.GetFramesAsyncEnumerator()); - - // await Task.Delay(50, stoppingToken); - } + public async Task StopAsync(CancellationToken cancellationToken) + { + await _connectionManager.Disconnect(); } } } \ No newline at end of file