微信小程序流式錄音RecorderManager.onFrameRecorded


錄音的API有那些?什么叫流式 & 非流式?

首先來說,在微信小程序的中有很多的有關錄音的API,了解這些API的特性,有助於我們在不同的產品需求下做技術選型。微信的錄音相關的api主要在這里: https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/wx.stopRecord.html ,表面上看主要是幾個大的分類:

  • wx.stopRecord(Object object)   // 停止錄音

  • wx.startRecord(Object object)     // 開始錄音

  • wx.getRecorderManager()     // 獲取全局唯一的錄音管理器 RecorderManager

  • RecorderManager   // 全局唯一的錄音管理器 RecorderManager

前兩個自然不用說了,代表的是第一個時代的錄音接口,注意的是從基礎庫 1.6.0 開始,本接口停止維護(推薦使用后面的),后兩個是配套的,通過調用wx.getRecorderManager() 獲取全局唯一的錄音管理器 RecorderManager,RecorderManager是一個單實例,他能提供如下方法:

RecorderManager.start(Object object)

開始錄音

RecorderManager.pause()

暫停錄音

RecorderManager.resume()

繼續錄音

RecorderManager.stop()

停止錄音

RecorderManager.onStart(function callback)

監聽錄音開始事件

RecorderManager.onResume(function callback)

監聽錄音繼續事件

RecorderManager.onPause(function callback)

監聽錄音暫停事件

RecorderManager.onStop(function callback)

監聽錄音結束事件

RecorderManager.onFrameRecorded(function callback)

監聽已錄制完指定幀大小的文件事件。如果設置了 frameSize,則會回調此事件。

RecorderManager.onError(function callback)

監聽錄音錯誤事件

RecorderManager.onInterruptionBegin(function callback)

監聽錄音因為受到系統占用而被中斷開始事件。以下場景會觸發此事件:微信語音聊天、微信視頻聊天。此事件觸發后,錄音會被暫停。pause 事件在此事件后觸發

RecorderManager.onInterruptionEnd(function callback)

監聽錄音中斷結束事件。在收到 interruptionBegin 事件之后,小程序內所有錄音會暫停,收到此事件之后才可再次錄音成功。

 

這些api,都是RecorderManager實例對象的方法,on開頭的都是事件回調函數,其他的就是對RecorderManager的操作方法,那么流式和非流式如何體現呢?首先說一下二者的本質區別:

  • 非流式:也可以理解為整包上傳,即有明確的開始和結束,也就是一個完整的音頻文件,我們要等音頻結束后在上傳或處理文件。這樣做的好處在於實現成本低,后台接收到完整文件后就可以直接處理了。前端交互頁比較好處理。但是一旦錄音時間比較長,文件就很大,上傳時就有可能超過請求報文的大小限制,后台接收了大文件,處理時間頁會比較長,會感覺接口響應超時,或比較慢,不適合實時性要求較高的場景。但較短的音頻比較適合非流式
  • 流式:是不等音頻結束,一旦采集音頻滿足一定的幀大小后就返回這一段的音頻(具體每一段是不是完整文件,要看系統平台)。我們在監聽函數中拿到這一段的音頻,就馬上上傳或處理-》得到處理結果,馬上呈現到頁面上,這樣一邊采集,一邊處理,一邊呈現結果。實時效果比較好。后台每次接受的文件大小也不會太大,處理速度也比較有保證。但是一方面前端交互比之前復雜,另一方面后台邏輯要能夠處理排序邏輯(保證接收的順序)。比較適合較長音頻的場景。

兩種方式,沒有好壞之分,只有使用場景的不同,需要結合自身業務選擇,下面來講一下如何用微信的api來實現非流式和流式錄音。

 

非流式如何實現錄音功能

既然是獲取一段完整的音頻文件,那么就需要有明確的開始和結束,可以參考這種寫法:

1 wx.startRecord({
2   success (res) {
3     const tempFilePath = res.tempFilePath
4   }
5 })
6 setTimeout(function () {
7   wx.stopRecord() // 結束錄音
8 }, 10000)

 注意2點,這里返回的是startRecord 的回調函數;返回的是一個臨時文件路徑。如果想上傳到后台處理,還需要調用微信的上傳文件的接口:wx.uploadFile(Object object)

 1 wx.startRecord({
 2   success (res) {
 3     const tempFilePath = res.tempFilePath
 4     wx.uploadFile({
 5       url: 'https://example.weixin.qq.com/upload', //僅為示例,非真實的接口地址
 6       filePath: tempFilePaths[0],
 7       name: 'file',
 8       formData: {
 9         'user': 'test'
10       },
11       success (res){
12         const data = res.data
13         //do something
14       }
15     })
16   }
17 })
18 setTimeout(function () {
19   wx.stopRecord() // 結束錄音
20 }, 10000)

如果你使用新版的接口(RecorderManager)的話,音頻文件的獲取:並不是在“start”里面而是“end”:

RecorderManager.onStop(function callback)

監聽錄音結束事件

參數

function callback

錄音結束事件的回調函數

參數

Object res
屬性 類型 說明
tempFilePath string 錄音文件的臨時路徑
duration number 錄音總時長,單位:ms
fileSize number 錄音文件大小,單位:Byte

也就是說文件是在回調函數的tempFilePath參數中,其他的也都同理。

流式如何實現錄音功能

由於音頻的獲取是不斷采集,每采集夠一定音頻幀數,就可以進行音頻處理,所以音頻的數據回調並不依賴明確的停止事件,而是滿足這幾點:

  • RecorderManager.start時配置好frameSize(也就是每一個分片的幀大小)指定幀大小,單位 KB。傳入 frameSize 后,每錄制指定幀大小的內容后,會回調錄制的文件內容,不指定則不會回調。暫僅支持 mp3 格式。
  • 因此format也要設置為mp3
  • 按照你和后台的約定,把采樣率、錄音通道數、編碼碼率設置好
  • 最后在 RecorderManager.onFrameRecorded(function callback) 的回調中獲取數據frameBuffer 和 是否為最后一幀isLastFrame 也就是https://developers.weixin.qq.com/miniprogram/dev/api/media/recorder/RecorderManager.onFrameRecorded.html

RecorderManager.onFrameRecorded(function callback)

監聽已錄制完指定幀大小的文件事件。如果設置了 frameSize,則會回調此事件。

參數

function callback

已錄制完指定幀大小的文件事件的回調函數

參數

Object res
屬性 類型 說明
frameBuffer ArrayBuffer 錄音分片數據
isLastFrame boolean 當前幀是否正常錄音結束前的最后一幀
 1 let seq = 0
 2 recorderManager.onFrameRecorded(res => {
 3   const {frameBuffer, isLastFrame} = res
 4   const base64Buffer = wepy.arrayBufferToBase64(frameBuffer)
 5   let url
 6   const data = {
 7     miniappname: 'fanyijun'
 8   }
 9   url = `${你的域名}/recorder/xxxxxx`
10   data.isEnd = isLastFrame ? 1 : 0
11   data.lang = recordObj.lang
12   data.sessionUuid = recordObj.sessionUuid
13   data.audio = base64Buffer
14   data.seq = seq
15   // console.log('recorderFrame url: ', url)
16   console.log('recorderFrame data: ', data)
17   wepy.request({
18     url,
19     data,
20     method: 'POST'
21   }).then(res => {
22     // 處理邏輯
23   }).catch(e => {
24     console.log('err:', e)
25   })
26   seq++
27 })

這樣,我們就將每次監聽到的音頻分片都上傳到服務器了 ,注意流式接口返回的不是音頻文件的臨時地址,而是frameBuffer,因為他不是完整的文件,微信本地也不會存儲這個文件。

兩種方式的區別在哪里

  調用風格 文件大小 返回值 音頻格式
非流式錄音接口 stop后在回調中拿到完整的文件 根據實際錄音時長(start和end的間隔)決定 臨時文件本地地址,徐調用上傳接口 mp3、aac、silk
流式錄音接口 onFrameRecordedzh中不斷獲取分片,持續獲取,持續處理 根據設定分片大小決定 frameBuffer,可普通請求上傳 mp3

非流式的分片到底是什么?

剛剛說到了分片,那么分片到底是什么,是不是獨立的文件,能不能單獨處理?我們一起來看一下:

首先我在內網咨詢了小程序這邊的同事,得到的反饋是流式分片返回的是frameBuffer,不能獨立使用。我同時也自己做了實驗驗證了一下。那就是那每一片frameBuffer,拿出來,分別寫入文件查看,因為Buffer是一種二進制數據類型,我們直接在node中就可以保存文件,因此我將一次錄音的相鄰的音頻分片分別寫入:

 1 var fs = require('fs');
 2 var file = require('./file');
 3 
 4 var data = file.data;
 5 
 6 var now = Date.now(); //獲取系統當前時間數值
 7 var savePath = './' + now + '.mp3'; //服務器存儲文件名
 8 let dataBuffer = new Buffer(data, 'base64');
 9 console.log(`data.length ${data.length}`);
10 console.log(`dataBuffer: leng ${dataBuffer.byteLength}`);
11 
12 fs.writeFile(savePath, aaa, function(err) {
13   if (err) {
14     console.log(err);
15   } 
16 });

 發現每個文件都可以獨立的播放,那么我們來試一下把這些frameBuffer鏈接在一起呢:

 1 var fs = require('fs');
 2 var file = require('./file');
 3 
 4 var datas = file.datas;
 5 
 6 var now = Date.now(); //獲取系統當前時間數值
 7 var savePath = './' + now + '.mp3'; //服務器存儲文件名
 8 // let dataBuffer = new Buffer(data, 'base64');
 9 // console.log(`data.length ${data.length}`);
10 // console.log(`dataBuffer: leng ${dataBuffer.byteLength}`);
11 
12 let aaa = Buffer.concat(
13   datas.map(x => {
14     return new Buffer(x, 'base64');
15   })
16 );
17 
18 fs.writeFile(savePath, aaa, function(err) {
19   if (err) {
20     console.log(err);
21   }
22 });

 發現這個鏈接frameBuffer后的音頻播放出來就是我說的內容,文件大小也符合。但是為什么不建議后台直接使用呢?

“分片分別轉碼在合並” vs 合並后再轉碼的MD5,並不相同(文件大小和長度相等),因此不能看作是同一音頻,后台同事和我的解釋就是會引入噪聲,目前來看仍然需要之前說的排序方案。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM