看到這裏的時候一定要理解並運行上文的《前端H5錄音實時幀回調-波形配置- Recorder.js》,這樣對Recorder有個概念。
1、通過onProcess回調可實現錄音的實時處理,onProcess的buffers參數內容為pcm數組(16位 LE小端模式 Little Endian),能直接流式的將數據進行上傳;
2、onProcess內的buffers參數為所有的PCM集合,如果是流式上傳我們只需要當前的分片即可,所以需要用到Recorder.SampleData方法來實時提取出pcm數據和轉換採樣率,在下面的例子中用send_chunk變量來標記。
3、如果直接使用pcm格式,初始化錄音對象時可以將type設置為"unknown",這樣用户更好回收(官網描述)
4、如果 type: 'unknown' 的話,結束錄音事件裏直接rec.close();就可以了,因為unknown的編碼格式不支持stop。
<template>
<div class="snow-page">
<div class="snow-inner">
<a-space direction="vertical" fill>
<!-- 波形繪製區域 -->
<div style="display: inline-block; vertical-align: bottom; border: 1px solid #cccccc">
<div style="width: 300px; height: 100px" ref="recwave"></div>
</div>
<a-space>
<a-button type="primary" @click="recOpen">打開錄音權限</a-button>
<a-button type="primary" @click="recStart">開始錄音</a-button>
<a-button @click="recStop">結束錄音</a-button>
</a-space>
</a-space>
</div>
</div>
</template>
<script setup lang="ts">
//必須引入的核心
import Recorder from "recorder-core";
//引入pcm格式支持文件
import "recorder-core/src/engine/pcm";
//可選的插件支持項,這個是波形可視化插件
import "recorder-core/src/extensions/waveview";
//ts import 提示:npm包內已自帶了.d.ts聲明文件(不過是any類型)
let wave: any; // 用於繪製波形
const recwave = ref(null); // 綁定到dom元素上
let rec: any; // Recorder實例
let send_chunk: any; // 上次分割點數據
let testSampleRate = 16000; // 採樣率
//重置環境,每次開始錄音時必須先調用此方法,清理環境
const RealTimeSendReset = () => {
send_chunk = null;
};
// 打開錄音
const recOpen = () => {
RealTimeSendReset();
// 創建錄音對象
rec = Recorder({
type: "unknown", //這裏特意使用unknown格式,方便清理內存
onProcess: (buffers: any, powerLevel: any, _: any, bufferSampleRate: any) => {
// 所有的pcm數據queue,緩存採樣率,是否結束
RealTimeSendTry(buffers, bufferSampleRate, false);
if (wave) {
wave.input(buffers[buffers.length - 1], powerLevel, bufferSampleRate);
}
}
});
if (!rec) {
alert("當前瀏覽器不支持錄音功能!");
return;
}
// 打開錄音,獲得權限
rec.open(
() => {
console.log("錄音已打開");
if (recwave.value) {
// 創建音頻可視化圖形繪製對象
wave = Recorder.WaveView({ elem: recwave.value });
}
},
(msg: any, isUserNotAllow: any) => {
// 用户拒絕了錄音權限,或者瀏覽器不支持錄音
console.log((isUserNotAllow ? "UserNotAllow," : "") + "無法錄音:" + msg);
}
);
};
const RealTimeSendTry = (buffers: any, bufferSampleRate: any, isClose: any) => {
//提取出新的pcm數據
let pcm = new Int16Array(0);
if (buffers.length > 0) {
//【關鍵代碼】借用SampleData函數進行數據的連續處理,採樣率轉換是順帶的,得到新的pcm數據
// send_chunk為上次分割點
let chunk = Recorder.SampleData(buffers, bufferSampleRate, testSampleRate, send_chunk);
send_chunk = chunk; // 保存本次分割點,用於下次使用
console.log("分片下標", chunk);
pcm = chunk.data; //此時的pcm就是原始的音頻16位pcm數據(小端LE),直接保存即為16位pcm文件、加個wav頭即為wav文件、丟給mp3編碼器轉一下碼即為mp3文件
}
//沒有指定固定的幀大小,直接把pcm發送出去即可
TransferUpload(pcm, isClose);
return;
};
//=====數據傳輸函數==========
const TransferUpload = (pcmFrame: any, isClose: any) => {
if (isClose && pcmFrame.length == 0) {
// 這裏就是數據發送完成,錄音結束的位置,可以停止ws了
// ws.send(arrayBuffer,true)
return; //如果不需要處理最後一幀數據,直接return不做任何處理
}
let arrayBuffer = pcmFrame.buffer;
console.log("pcm二進制", arrayBuffer);
//可以實現
//WebSocket send(arrayBuffer) ...
//WebRTC send(arrayBuffer) ...
//XMLHttpRequest send(arrayBuffer) ...
//最後一次調用發送,此時的pcmFrame可以認為是最後一幀
if (isClose) {
console.log("最後一次調用", pcmFrame, pcmFrame.length);
// 比如再發送一次socket
// ws.send(arrayBuffer,false)
}
};
// 開始錄音
const recStart = () => {
if (!rec) return console.error("未打開錄音");
rec.start();
console.log("已開始錄音");
};
// 結束錄音
const recStop = () => {
if (!rec) return console.error("未打開錄音");
rec.close(); // 關閉錄音,釋放錄音資源
rec = null;
RealTimeSendTry([], 0, true); //最後一次發送
};
</script>
<style lang="scss" scoped></style>