当前位置: 首页 > news >正文

使用 SignalR 向前端推送图像

我的早期方案
public class VideoService
{const string VideoFilePath = "D:\\Users\\xx\\Desktop\\";/// <summary>/// 运行中/// </summary>public bool IsRunning { get; private set; } = false;object locker = new();CancellationTokenSource cts = new();Memory<byte>? _memoryCache;/// <summary>/// 图片base64;/// </summary>public string ImageBase64 => _memoryCache == null ? "" : Convert.ToBase64String(_memoryCache.Value.Span);/// <summary>/// 启动/// </summary>public void Startup(){if (IsRunning) return;Monitor.Enter(locker);Task.Factory.StartNew(() =>{IsRunning = true;int index = 1;while (!cts.Token.IsCancellationRequested){// 长时间运行的逻辑string filePath = Path.Combine(VideoFilePath, $"{index}.jpg");if (index >= 2) index = 1; else index++;if (File.Exists(filePath)){var bytesRead = File.ReadAllBytes(filePath);_memoryCache = new Memory<byte>(bytesRead, 0, bytesRead.Length);}}}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap().ContinueWith(t => {IsRunning = false;cts = new();Monitor.Exit(locker);});}/// <summary>/// 关闭/// </summary>public void Shutdown(){if (!IsRunning) return;cts.Cancel();}
}

然后从前端轮询 ImageBase64;这样的结果是图像非常不连贯

 

改造后使用  SignalR 向前端推送;

依赖 Microsoft.AspNetCore.SignalR 和 Microsoft.AspNetCore.SignalR.Client ; 在 nuget 下载

1 创建一个 Hub

/// <summary>
/// 主要用来映射连接端点
/// </summary>
public class VideoStreamHub : Hub
{// 可以在这里实现客户端连接管理
}

2 启用 SignalR 服务,并注册端点

image

3 改造 service

public class VideoService
{const string VideoFilePath = "D:\\Users\\xx\\Desktop\\";private readonly IHubContext<VideoStreamHub> _hubContext;public VideoService(IHubContext<VideoStreamHub> hubContext){_hubContext = hubContext;}/// <summary>/// 运行中/// </summary>public bool IsRunning { get; private set; } = false;object locker = new();CancellationTokenSource cts = new();Memory<byte>? _memoryCache;/// <summary>/// 图片base64;/// </summary>public string ImageBase64 => _memoryCache == null ? "" : Convert.ToBase64String(_memoryCache.Value.Span);/// <summary>/// 启动/// </summary>public void Startup(){if (IsRunning) return;Monitor.Enter(locker);cts = new();Task.Factory.StartNew(async () =>{IsRunning = true;int index = 1;while (!cts.Token.IsCancellationRequested){// 长时间运行的逻辑string filePath = Path.Combine(VideoFilePath, $"{index}.jpg");if (index >= 2) index = 1; else index++;if (File.Exists(filePath)){var bytesRead = File.ReadAllBytes(filePath);await _hubContext.Clients.All.SendAsync("ReceiveFrame", Convert.ToBase64String(bytesRead), cts.Token);  // 向前端的接收方法推送流}await Task.Delay(30, cts.Token);}cts.Token.ThrowIfCancellationRequested();}, cts.Token, TaskCreationOptions.LongRunning, TaskScheduler.Default).Unwrap().ContinueWith(t => {if (cts.Token.IsCancellationRequested){IsRunning = false;Monitor.Exit(locker);}});}/// <summary>/// 关闭/// </summary>public void Shutdown(){if (!IsRunning) return;cts.Cancel();}
}

 

4 前端注册接收方法

@page "/"
@using BlazorAppVideo.Services
@using Microsoft.AspNetCore.SignalR.Client
@inject VideoService _VideoService
@implements IDisposable
@inject NavigationManager NavManager<PageTitle>Home</PageTitle>
<img src="@currentFrame" alt="Blazor logo" />@code {string currentFrame { get; set; } = string.Empty;private HubConnection? hubConnection;CancellationTokenSource source = new();protected override async Task OnAfterRenderAsync(bool firstRender){await base.OnAfterRenderAsync(firstRender);if (firstRender){// 启动视频服务
            _VideoService.Startup();// 创建SignalR连接hubConnection = new HubConnectionBuilder().WithUrl(NavManager.ToAbsoluteUri("/videoStreamHub")).Build();// 注册接收帧的处理方法hubConnection.On<string>("ReceiveFrame", (frame) =>{currentFrame = $"data:image/jpeg;base64,{frame}";InvokeAsync(StateHasChanged);       // 接收到新帧后更新UI
            });await hubConnection.StartAsync();}}public void Dispose(){hubConnection?.DisposeAsync();}
}

 

 改造后图像非常连贯,可用于视觉项目,从服务端推送采集的图像到前端

 

http://www.hskmm.com/?act=detail&tid=19309

相关文章:

  • 隐私保护与联邦学习文献阅读
  • Java实习模拟面试|离散数学|概率论|金融英语|数据库实战|职业规划|期末冲刺|今日本科计科要闻速递:技术分享与学习指南 - 实践
  • 学术写作
  • 2025.9.27
  • 9.27(课后作业
  • 详细介绍:【序列晋升】45 Spring Data Elasticsearch 实战:3 个核心方案破解索引管理与复杂查询痛点,告别低效开发
  • 详细介绍:python+django/flask+uniapp基于微信小程序的瑜伽体验课预约系统
  • 生成算数问题*30
  • 6379:统计学生信息(使用动态链表完成)
  • 详细介绍:云原生 vs 传统部署
  • 单链表
  • 课后作业1-3
  • GNSS精度判断和协方差矩阵 - MKT
  • Insightly模板页面存储型XSS漏洞分析与复现
  • 记录 | 关于陪伴型交互AI的一些探讨
  • 课后作业
  • luogu P1719 最大加权矩形
  • CF2065D Skibidus and Sigma
  • 微信二次开发个人号api
  • 课后作业2(动手动脑,课后实验性问题)
  • 从零开始构建图注意力网络:GAT算法原理与数值实现详解
  • 关于Leetcode 812题的简单思考
  • Laravel5.8 利用 snappyPDF 生成PDF文件
  • 25秋周总结4
  • Python 潮流周刊#121:工程师如何做出高效决策?
  • 饥荒联机版
  • iSCSI网络存储——基于VM17下麒麟V10SP1与SP2的共享配置
  • 微信二次开发文档
  • CSP-S1 2025
  • 金币