Blazor应用如何传递音频流实现Azure认知服务连续语音转文本
Blazor 客户端音频流转服务端实现方案
针对你用Azure Speech做连续语音转文本的场景,核心要求是低延迟、分片连续传输,不能等完整音频生成后一次性上传,否则连续识别会出现卡顿、延迟过高的问题,以下是可直接落地的实现方案:
方案选型
- 优先选SignalR双向流传输:Blazor原生集成通信能力,无需额外搭建服务,延迟最低,最适配实时音频场景
- 备选为分片HTTP上传:适合部署环境不支持WebSocket的场景,延迟略高,需要自行维护会话和分片顺序
方案1:SignalR流传输(推荐)
你用的16kHz、16位、单声道PCM格式,每100ms音频对应3200字节数据,传输时单分片大小控制在100ms~300ms数据量即可,分片太大会拉高延迟,太小会产生过多无效请求开销。
- 服务端先定义Speech识别Hub,支持流式接收音频分片、实时返回识别结果:
public class SpeechRecognitionHub : Hub { public async Task<Channel<string>> StartContinuousRecognition(IAsyncEnumerable<byte[]> audioChunks, CancellationToken ct) { var resultChannel = Channel.CreateUnbounded<string>(); // 复用你已有的Azure Speech配置逻辑 var audioFormat = AudioStreamFormat.GetWaveFormatPCM(16000, 16, 1); var pushStream = AudioInputStream.CreatePushStream(audioFormat); var audioConfig = AudioConfig.FromStreamInput(pushStream); // 后台消费客户端上传的音频分片,直接写入Speech输入流 _ = Task.Run(async () => { await foreach (var chunk in audioChunks.WithCancellation(ct)) { pushStream.Write(chunk); } // 客户端传完所有分片后关闭输入流,触发识别收尾 pushStream.Close(); }, ct); // 补全Speech识别器的事件绑定逻辑,把实时识别出的文本写入resultChannel返回给客户端即可 return resultChannel; } }
记得在Program.cs里注册Hub:app.MapHub<SpeechRecognitionHub>("/speech-recog");
- 客户端侧连接Hub,边采集音频边推流,同时实时接收识别结果:
// 初始化Hub连接 var hubConn = new HubConnectionBuilder() .WithUrl("/speech-recog") .Build(); await hubConn.StartAsync(); // 建通道缓存待上传的音频分片 var audioUploadChannel = Channel.CreateUnbounded<byte[]>(); // 调用服务端识别方法,拿到识别结果流 var resultStream = hubConn.StreamAsync<string>("StartContinuousRecognition", audioUploadChannel.Reader.ReadAllAsync()); // 本地从audioStream读到PCM分片时,直接写入上传通道即可,不需要攒包 // 例:读到3200字节的100ms PCM数据,直接执行 await audioUploadChannel.Writer.WriteAsync(pcmChunk); // 音频采集结束时标记上传完成 audioUploadChannel.Writer.Complete(); // 实时消费识别结果更新UI await foreach (var text in resultStream) { // 把text绑定到页面显示字段即可 }
注意事项:
- 客户端不需要做任何音频转码,直接传原始PCM分片就行,和Azure Speech要求的输入格式完全匹配,省掉两端转码开销
- 页面跳转/组件销毁时记得触发CancellationToken,释放Speech识别器、流和Hub连接资源,避免内存泄漏
- Blazor Server模式下SignalR复用现有circuits连接,额外开销几乎为0;Blazor WASM模式下自动走WebSocket传输,延迟可以控制在200ms以内,完全满足实时识别要求
方案2:分片HTTP上传(备选)
如果部署环境不支持WebSocket,可以用分片POST的方式实现:
- 客户端给每个识别会话生成唯一ID,每采集到固定大小的PCM分片,就按顺序带会话ID、分片序号POST到服务端接口
- 服务端按会话ID维护对应的
PushStream实例,收到分片后校验序号、按顺序写入对应流 - 识别结果可以通过SSE(服务器发送事件)推回客户端,或者短轮询拉取
这个方案需要自行处理分片乱序、会话过期、流资源释放的逻辑,延迟比SignalR方案高100~300ms,非必要不选。
踩坑提醒:不要尝试直接把完整Stream对象通过Blazor组件参数、JS互操作直接传值给服务端,Blazor的跨端传值走序列化逻辑,会把整个流内容一次性加载到内存,既没法实现实时连续识别,还容易触发内存溢出。
内容的提问来源于stack exchange,提问作者Harini Muralidharan
相关产品推荐
相关产品推荐

