为Blob添加元数据:解决MediaRecorder分块音频处理异常问题
实时音频转文本的MediaRecorder数据块问题
我用MediaRecorder获取用户麦克风输入,每次收到数据就实时发送到后端处理,无需等待音频文件完整。但将音频发送给Whisper API时,只有第一块数据能正常识别,后续数据块均无法正常处理。排查后发现,MediaRecorder仅在第一个Blob中设置元数据,导致后续音频文件损坏。
我尝试过用node-lame(需依赖外部程序)、wavefile(不支持缓冲区)转换Blob,但都未成功。目前想到两种解决方案:
- 为每个Blob添加元数据,但查阅资料后仍不知如何手动设置音频头部
- 找到合理调用MediaRecorder.start和MediaRecorder.stop的方式,避免数据丢失
以下是当前代码:
前端组件代码(speech-to-text.component.ts)
import { Component, OnInit } from '@angular/core'; import { SpeechService } from '../service/speech.service'; @Component({ selector: 'app-speech-to-text', templateUrl: './speech-to-text.component.html', styleUrls: ['./speech-to-text.component.scss'], }) export class SpeechToTextComponent implements OnInit { mediaRecorder!: MediaRecorder; constructor(private speechService: SpeechService) {} async ngOnInit() { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); this.mediaRecorder = new MediaRecorder(stream); this.mediaRecorder.addEventListener('dataavailable', (event) => { this.speechService .sendAudioBlob(new Blob([event.data], { type: 'audio/mp3' })) .then((result) => console.log(result)); }); } record() { this.mediaRecorder.start(1000); } stop() { this.mediaRecorder.stop(); } }
前端服务代码(speech.service.ts)
import { Injectable } from '@angular/core'; import { AppHttpClientService } from './app-http-client/app-http-client.service'; @Injectable({ providedIn: 'root', }) export class SpeechService { constructor(private httpService: AppHttpClientService) {} async sendAudioBlob(blob: Blob) { const formData = new FormData(); formData.append('blob', blob); const result = await this.httpService.post('speeches/text', formData); return result; } }
后端服务代码(speeches.service.js)
const openai = require('../models/openai'); const fs = require('fs'); const formidable = require('formidable'); const { MODEL_NAME } = require('./speech.constants'); const fileDirectory = `${__dirname}/../../../uploads`; const SpeechesService = { speechToText: async (req) => { const fileName = `${Date.now()}.mp3`; const form = new formidable.Formidable({ uploadDir: fileDirectory, filename: () => fileName, }); await form.parse(req); const resp = await openai .createTranscription(fs.createReadStream(`${fileDirectory}/${fileName}`), MODEL_NAME) .catch(); SpeechesService.deleteAudio(fileName); return resp.data; }, deleteAudio: async (fileName) => fs.unlink(`${fileDirectory}/${fileName}`, () => {}), }; module.exports = SpeechesService;
内容的提问来源于stack exchange,提问作者Firmin Exertier
相关产品推荐
相关产品推荐

