Xamarin.iOS中Google Cloud Speech流式语音转文本遇gRPC及Sharpie导入问题
我之前也帮开发者解决过类似的Xamarin.iOS对接Google Speech API的问题,你遇到的gRPC兼容和Sharpie报错确实是这个场景下的常见坑,给你两种可行的解决方案,按需选择:
方案一:绕开gRPC,使用REST流式API(推荐)
官方的Google.Cloud.Speech.V1依赖的gRPC库在Xamarin.iOS上的原生支持一直有兼容性问题,切换到REST流式接口是最稳妥的方式,它同样支持实时音频传输和即时识别结果返回。
1. 准备Google Cloud认证
- 先在Google Cloud Console开启Speech-to-Text服务,生成JSON格式的服务账号密钥,将密钥文件添加到Xamarin.iOS项目中,设置文件的生成操作为
BundleResource。 - 通过以下代码获取认证令牌:
using Google.Apis.Auth.OAuth2; using System.Net.Http.Headers; using System.Threading.Tasks; private async Task<string> GetGoogleAuthToken() { var credential = GoogleCredential.FromFile("your-service-account-key.json") .CreateScoped("https://www.googleapis.com/auth/cloud-platform"); var token = await credential.UnderlyingCredential.GetAccessTokenForRequestAsync(); return token; }
2. 实时音频捕获与流式传输
Xamarin.iOS可以用AVAudioEngine捕获音频,转换为Google要求的LINEAR16编码、16kHz采样率、单声道格式,然后分块发送到REST接口:
using AVFoundation; using System.IO; using System.Net.Http; private AVAudioEngine _audioEngine; private HttpClient _httpClient; private StreamWriter _responseWriter; public async Task StartStreamingRecognition() { // 初始化HttpClient和认证 var authToken = await GetGoogleAuthToken(); _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authToken); // 第一步:发送识别配置请求 var configPayload = @" { ""config"": { ""encoding"": ""LINEAR16"", ""sampleRateHertz"": 16000, ""languageCode"": ""zh-CN"", ""enableAutomaticPunctuation"": true }, ""audio"": {} }"; var content = new StringContent(configPayload, System.Text.Encoding.UTF8, "application/json"); var request = new HttpRequestMessage(HttpMethod.Post, "https://speech.googleapis.com/v1/speech:streamingRecognize") { Content = content }; var response = await _httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); response.EnsureSuccessStatusCode(); // 准备音频流写入和响应读取 var responseStream = await response.Content.ReadAsStreamAsync(); _responseWriter = new StreamWriter(responseStream); var responseReader = new StreamReader(responseStream); // 初始化音频引擎捕获音频 InitializeAudioEngine(responseReader); } private void InitializeAudioEngine(StreamReader responseReader) { _audioEngine = new AVAudioEngine(); var inputNode = _audioEngine.InputNode; var inputFormat = inputNode.InputFormatForBus(0); // 转换为目标音频格式 var targetFormat = new AVAudioFormat(AVAudioCommonFormat.PCMFormatInt16, 16000, 1, true); var converter = new AVAudioConverter(inputFormat, targetFormat); inputNode.InstallTapOnBus(0, 4096, inputFormat, async (buffer, time) => { var convertedBuffer = new AVAudioPcmBuffer(targetFormat, buffer.FrameLength); converter.ConvertToBuffer(convertedBuffer, buffer, out var error); if (error != null) { Console.WriteLine($"音频转换错误: {error.LocalizedDescription}"); return; } // 将音频数据转为Base64并发送 var audioBytes = convertedBuffer.GetChannelData(0).ToArray((int)(convertedBuffer.FrameLength * 2)); var base64Audio = Convert.ToBase64String(audioBytes); var audioPayload = $"{{\"audio\": {{\"content\": \"{base64Audio}\"}}}}"; await _responseWriter.WriteLineAsync(audioPayload); await _responseWriter.FlushAsync(); // 读取并解析识别结果 var resultJson = await responseReader.ReadLineAsync(); if (!string.IsNullOrEmpty(resultJson)) { ParseAndDisplayRecognitionResult(resultJson); } }); _audioEngine.Prepare(); _audioEngine.StartAndReturnError(out var startError); if (startError != null) { Console.WriteLine($"音频引擎启动失败: {startError.LocalizedDescription}"); } } // 解析识别结果并展示的示例方法 private void ParseAndDisplayRecognitionResult(string resultJson) { // 这里用JSON库(比如Newtonsoft.Json)解析resultJson,提取识别文本 // 示例: var resultObj = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(resultJson); var transcript = resultObj.results[0].alternatives[0].transcript; // 更新UI展示识别结果(注意在主线程操作) InvokeOnMainThread(() => { YourResultLabel.Text = transcript; }); }
3. 配置必要权限
在Info.plist中添加麦克风权限申请:
<key>NSMicrophoneUsageDescription</key> <string>需要访问麦克风进行实时语音识别</string>
方案二:修复gRPC与Sharpie报错(适合坚持用官方NuGet的场景)
如果一定要使用Google.Cloud.Speech.V1的gRPC实现,可以尝试以下步骤修复:
1. 更新依赖包与项目配置
- 确保安装最新版本的
Google.Cloud.Speech.V1、Grpc.Core和Grpc.Core.ApiNuGet包。 - 将Xamarin.iOS项目的目标框架设置为iOS 15+,并把链接器行为改为
链接SDK程序集(避免gRPC原生库被误删)。
2. 手动处理gRPC原生绑定
"Sharpie import not found"报错是因为NuGet包中的Objective-C绑定代码依赖Sharpie生成的头文件,但Xamarin环境未正确配置。可以手动重新生成绑定:
- 从gRPC官方GitHub下载iOS原生库的头文件。
- 使用Sharpie工具生成绑定代码:
sharpie bind -sdk iphoneos16.0 -output GrpcBindings -namespace Grpc.Core.Native *.h - 将生成的绑定项目添加到解决方案,替换NuGet包中的gRPC绑定。
3. 初始化Speech客户端并流式识别
修复绑定后,可以使用官方SDK的流式API:
using Google.Cloud.Speech.V1; using Grpc.Core; private async Task StartGrpcStreamingRecognition() { var credential = GoogleCredential.FromFile("your-service-account-key.json"); var channel = new Channel(SpeechClient.DefaultEndpoint.ToString(), credential.ToChannelCredentials()); var speechClient = SpeechClient.Create(channel); var streamingCall = speechClient.StreamingRecognize(); // 发送配置 await streamingCall.WriteAsync(new StreamingRecognizeRequest { Config = new RecognitionConfig { Encoding = RecognitionConfig.Types.AudioEncoding.Linear16, SampleRateHertz = 16000, LanguageCode = "zh-CN" } }); // 捕获音频并发送(这里复用之前的AVAudioEngine捕获逻辑) InitializeAudioEngineForGrpc(streamingCall); // 读取识别结果 await foreach (var response in streamingCall.ResponseStream.ReadAllAsync()) { foreach (var result in response.Results) { foreach (var alternative in result.Alternatives) { InvokeOnMainThread(() => { YourResultLabel.Text = alternative.Transcript; }); } } } }
内容的提问来源于stack exchange,提问作者Romain Rastel
相关产品推荐
相关产品推荐

