You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Azure Speech识别在Angular+.NET Core环境下失效问题求助

Angular→.NET Core→Azure Speech PushStream实时语音识别失败

问题概述

我们尝试基于Azure Speech的PushStream实现实时语音识别:在Angular 17应用中通过navigator.mediaDevices.getUserMedia()录制音频,将音频发送至.NET 8.0后端后,Azure无法识别该音频内容。

场景对比

  • 可正常运行的场景:Angular/React + Node.js 18后端,通过socket.io传输音频流,能无缝对接Azure Speech转文本识别
  • 失败场景:切换为.NET Core后端后,无论是使用SignalR(将base64编码音频转为byte[]传输)还是原生WebSocket,音频均无法被Azure识别

限制条件(不采用Azure JS SDK的原因)

  • 后端团队仅具备.NET技术能力,无法维护前端侧的Azure Speech集成逻辑
  • 出于安全考虑,禁止在前端硬编码Azure Speech的配置信息

可正常运行的Angular+Node.js+Azure代码

Angular 前端代码(音频录制与传输)

import { Component, OnInit } from '@angular/core';
import * as io from 'socket.io-client';

@Component({
  selector: 'app-speech-recognition',
  templateUrl: './speech-recognition.component.html',
  styleUrls: ['./speech-recognition.component.css']
})
export class SpeechRecognitionComponent implements OnInit {
  private socket: any;
  private mediaRecorder: MediaRecorder | null = null;

  ngOnInit(): void {
    this.socket = io('http://localhost:3000');
  }

  startRecording(): void {
    navigator.mediaDevices.getUserMedia({ audio: true })
      .then(stream => {
        this.mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/wav' });
        this.mediaRecorder.ondataavailable = (event) => {
          if (event.data.size > 0) {
            const reader = new FileReader();
            reader.onload = () => {
              const base64Audio = reader.result as string;
              this.socket.emit('audio-stream', base64Audio);
            };
            reader.readAsDataURL(event.data);
          }
        };
        this.mediaRecorder.start(100);
      })
      .catch(err => console.error('录音启动失败:', err));
  }

  stopRecording(): void {
    this.mediaRecorder?.stop();
    this.socket.emit('audio-stop');
  }
}

Node.js 后端代码(对接Azure Speech)

const express = require('express');
const http = require('http');
const { Server } = require('socket.io');
const sdk = require('microsoft-cognitiveservices-speech-sdk');

const app = express();
const server = http.createServer(app);
const io = new Server(server, { cors: { origin: "*" } });

const speechConfig = sdk.SpeechConfig.fromSubscription('YOUR_AZURE_KEY', 'YOUR_AZURE_REGION');
speechConfig.speechRecognitionLanguage = 'zh-CN';

io.on('connection', (socket) => {
  console.log('客户端已连接');
  
  const pushStream = sdk.AudioInputStream.createPushStream();
  const audioConfig = sdk.AudioConfig.fromStreamInput(pushStream);
  const recognizer = new sdk.SpeechRecognizer(speechConfig, audioConfig);

  recognizer.recognized = (s, e) => {
    if (e.result.reason === sdk.ResultReason.RecognizedSpeech) {
      socket.emit('recognition-result', e.result.text);
    }
  };

  recognizer.startContinuousRecognitionAsync();

  socket.on('audio-stream', (base64Audio) => {
    const audioBuffer = Buffer.from(base64Audio.split(',')[1], 'base64');
    pushStream.write(audioBuffer);
  });

  socket.on('audio-stop', () => {
    recognizer.stopContinuousRecognitionAsync();
    pushStream.close();
  });

  socket.on('disconnect', () => {
    recognizer.stopContinuousRecognitionAsync();
    pushStream.close();
    console.log('客户端已断开');
  });
});

server.listen(3000, () => {
  console.log('后端服务运行在端口3000');
});

无法运行的Angular+.NET Core+Azure代码

Angular 前端代码(SignalR传输)

import { Component, OnInit } from '@angular/core';
import { HubConnection, HubConnectionBuilder } from '@microsoft/signalr';

@Component({
  selector: 'app-speech-recognition',
  templateUrl: './speech-recognition.component.html',
  styleUrls: ['./speech-recognition.component.css']
})
export class SpeechRecognitionComponent implements OnInit {
  private hubConnection: HubConnection | null = null;
  private mediaRecorder: MediaRecorder | null = null;

  ngOnInit(): void {
    this.hubConnection = new HubConnectionBuilder()
      .withUrl('http://localhost:5000/speechHub')
      .build();

    this.hubConnection.start().catch(err => console.error('SignalR连接失败:', err));

    this.hubConnection.on('RecognitionResult', (result) => {
      console.log('识别结果:', result);
    });
  }

  startRecording(): void {
    navigator.mediaDevices.getUserMedia({ audio: true })
      .then(stream => {
        this.mediaRecorder = new MediaRecorder(stream, { mimeType: 'audio/wav' });
        this.mediaRecorder.ondataavailable = (event) => {
          if (event.data.size > 0) {
            const reader = new FileReader();
            reader.onload = () => {
              const base64Audio = reader.result as string;
              this.hubConnection?.invoke('SendAudioStream', base64Audio);
            };
            reader.readAsDataURL(event.data);
          }
        };
        this.mediaRecorder.start(100);
      })
      .catch(err => console.error('录音启动失败:', err));
  }

  stopRecording(): void {
    this.mediaRecorder?.stop();
    this.hubConnection?.invoke('StopAudioStream');
  }
}

.NET Core 后端代码(SignalR + Azure Speech)

using Microsoft.AspNetCore.SignalR;
using Microsoft.CognitiveServices.Speech;
using Microsoft.CognitiveServices.Speech.Audio;

namespace SpeechRecognitionBackend.Hubs
{
    public class SpeechHub : Hub
    {
        private SpeechRecognizer? _recognizer;
        private PushAudioInputStream? _pushStream;

        public async Task SendAudioStream(string base64Audio)
        {
            if (_pushStream == null)
            {
                var speechConfig = SpeechConfig.FromSubscription("YOUR_AZURE_KEY", "YOUR_AZURE_REGION");
                speechConfig.SpeechRecognitionLanguage = "zh-CN";
                
                _pushStream = AudioInputStream.CreatePushStream();
                var audioConfig = AudioConfig.FromStreamInput(_pushStream);
                _recognizer = new SpeechRecognizer(speechConfig, audioConfig);

                _recognizer.Recognized += (s, e) =>
                {
                    if (e.Result.Reason == ResultReason.RecognizedSpeech)
                    {
                        Clients.Caller.SendAsync("RecognitionResult", e.Result.Text);
                    }
                };

                await _recognizer.StartContinuousRecognitionAsync();
            }

            byte[] audioBytes = Convert.FromBase64String(base64Audio.Split(',')[1]);
            _pushStream.Write(audioBytes, 0, audioBytes.Length);
        }

        public async Task StopAudioStream()
        {
            if (_recognizer != null)
            {
                await _recognizer.StopContinuousRecognitionAsync();
                _recognizer.Dispose();
                _pushStream?.Close();
                _pushStream?.Dispose();
            }
        }
    }
}

后端Program.cs 配置

var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSignalR();
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAll", policy =>
    {
        policy.AllowAnyOrigin()
              .AllowAnyMethod()
              .AllowAnyHeader();
    });
});

var app = builder.Build();

app.UseCors("AllowAll");
app.MapHub<SpeechHub>("/speechHub");

app.Run();

内容的提问来源于stack exchange,提问作者Raj

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 15:10:56