5

使用浏览器 Web API,我想设置MediaDevices.getUserMedia 约束属性,适合录制音频语音(语音消息),例如设置这些参数:

  • 单核细胞增多症
  • 16位
  • 16KHz

这是我的代码:

   const mediaStreamConstraints = {
       audio: {
         channelCount: 1,
         sampleRate: 16000,
         sampleSize: 16,
         volume: 1
       },

       video: false
   }

   navigator.mediaDevices.getUserMedia(mediaStreamConstraints)
     .catch( err => serverlog(`ERROR mediaDevices.getUserMedia: ${err}`) )
     .then( stream => {

        // audio recorded as Blob 
        // and the binary data are sent via socketio to a nodejs server
        // that store blob as a file (e.g. audio/inp/audiofile.webm)

      } )

录制的剪辑被抓取并存储(使用MediaRecorderAPI),最终发送到 nodejs 服务器,在那里将 blob 保存为文件并进行处理(应用程序是语音机器人)。

出了点问题,WebM 保存的文件没有所需的参数:

$ mediainfo audio/inp/audiofile.webm
General
Complete name                            : audio/inp/audiofile.webm
Format                                   : WebM
Format version                           : Version 4 / Version 2
File size                                : 2.04 KiB
Writing application                      : Chrome
Writing library                          : Chrome
IsTruncated                              : Yes

Audio
ID                                       : 1
Format                                   : Opus
Codec ID                                 : A_OPUS
Channel(s)                               : 1 channel
Channel positions                        : Front: C
Sampling rate                            : 48.0 kHz
Bit depth                                : 32 bits
Compression mode                         : Lossy
Language                                 : English
Default                                  : Yes
Forced                                   : No

例如

Sampling rate                            : 48.0 kHz
Bit depth                                : 32 bits

但是约束意味着不同的值:

Sampling rate                            : 16 kHz
Bit depth                                : 16 bits

Audio(audioUrl(blob)).play()同样,用 anew 播放的blob也不播放。诡异的。但是,如果约束只是:

const mediaStreamConstraints = { audio: true }

我检查了浏览器控制台,没有看到任何navigator.mediaDevices.getUserMedia(mediaStreamConstraints)API 调用错误。

顺便说一句,我在这里遵循了指导方针:

请注意,我的用户代理是:(Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/81.0.4044.138 Safari/537.36我使用的是最后一个 Brave 浏览器版本)。


在我看来,浏览器不允许的任何音频约束设置:

  • 破坏了音频斑点,
  • 没有引发错误异常(我同时捕获了navigator.mediaDevices.getUserMedia()new MediaRecorder(...)。这不是最后一个错误吗?

我的问题是:

有什么方法可以根据要求获得采样率/位深度?

或者音频格式是“硬编码”/由浏览器实现决定的?


顺便说一句,音频参数格式的原因是因为我想最小化音频 blob 大小以最小化浏览器客户端和服务器之间的 websocket 通信中的带宽,优化语音(语音消息)的音频 blob 交换

4

3 回答 3

4

首先检查浏览器的功能:

let stream = await navigator.mediaDevices.getUserMedia({audio: true});
let track = stream.getAudioTracks()[0];
console.log(track.getCapabilities());

演示输出:

autoGainControl: (2) [true, false]
channelCount: {max: 2, min: 1}
deviceId: "default"
echoCancellation: (2) [true, false]
groupId: "1e76386ad54f9ad3548f6f6c14c08e7eff6753f9362d93d8620cc48f546604f5"
latency: {max: 0.01, min: 0.01}
noiseSuppression: (2) [true, false]
sampleRate: {max: 48000, min: 48000}
sampleSize: {max: 16, min: 16}
于 2020-07-29T16:50:05.123 回答
1

在实例化 MediaRecorder 之前,尝试在流中的音频媒体轨道上设置音频约束。

像这样的东西,没有调试:

const constraints = {
   audio: {
     channelCount: 1,
     sampleRate: 16000,
     sampleSize: 16,
     volume: 1
   },

navigator.mediaDevices.getUserMedia({audio:true})
 .catch( err => serverlog(`ERROR mediaDevices.getUserMedia: ${err}`) )
 .then( stream => {

    const audioTracks = stream.getAudioTracks()
    if (audioTracks.length !== 1) throw new Error ('too many tracks???')
    const audioTrack = audioTracks[0]       
    audioTrack.applyConstraints (constraints)
    .then (()=> {

         const mediaRecorder = new MediaRecorder(stream)
         /* etc etc etc */
      } )
    .catch(console.error) /* you might get constraint failure here. */
  } )

话虽如此,Opus 音频编解码器在将语音压缩到合理大小方面做得很好。仅仅因为它 sez 48kHz x 32bits 并不意味着它使用那么多带宽;音频信号被压缩。

并且,在最新版本的 Google Chrome 和/或 Firefox 上试用。这种媒体的东西正在积极发展中。

于 2020-06-22T17:14:57.857 回答
0

O.Jones 的答案是正确的,但可以通过在抓取 userMedia 对象时直接设置约束来更简洁。

const constraints = {
    audio: {
        channelCount: 1,
        sampleRate: 16000,
        sampleSize: 16,
        volume: 1
    }
}

navigator.mediaDevices.getUserMedia({audio: constraints}) 
    .then( stream => {
        // Do something with the stream...
    })
    .catch( err => serverlog(`ERROR mediaDevices.getUserMedia: ${err}`) )
于 2021-03-09T17:20:22.073 回答