H5利用navigator.mediaDevices实现语音功能记录

一、需求功能点描述

按住说话 松开发送 上滑取消 语音时长超过3分钟,自动结束录入并发送。

采用方案:使用Web API navigator.mediaDevices 实现该功能。

H5语音功能

二、开发过程

1、获取麦克风权限

起初是在按住说话开始录音阶段才获取麦克风权限,但是由于测试过程中出现bug:按住说话时获取权限当授权之后 没长按就能录音 因此前置获取权限(在文本输入框切换成语音输入框时获取麦克风权限)

function captureMicrophone() {
  navigator.mediaDevices.getUserMedia({ audio: true })
    .then(() => {
    })
    .catch(() => { // 禁止权限
      showDialog({
        title: '暂无麦克风权限',
        message: '推荐使用手机自带浏览器\n打开该页面',
        confirmButtonText: '知道了',
      }).then(() => {
        // on close
      })
    })
}

2、开始录音

侦听touchstart 触摸元素事件,开始录音。

// 按住说话开始录语音
function startRecording(event) {
  event.preventDefault() //阻止默认事件,避免了安卓设备长按会出现选中文字剪贴板
  posStart.value = event.touches[0].pageY// 获取起点坐标(用于上滑取消发送语音判断)
  // 语音输入
  try {
    navigator.mediaDevices.getUserMedia({ audio: true })
      .then((stream) => {
        touchStart.value = true
        createTimer() // 计时三分钟
        audioStream.value = stream
        mediaRecorder.value = new MediaRecorder(stream)
        mediaRecorder.value.start()
        audioChunks.value = []
        mediaRecorder.value.addEventListener('dataavailable', (event) => {
          audioChunks.value.push(event.data)
        })
      })
      .catch(() => { // 禁止权限
        touchStart.value = false
        resetTimer() //清理计时器
      })
  }
  catch (error) {
    // console.log("获取语音error",error);
  }
}

对于 移动端长按会出现选中文字剪贴板的问题:

(1)、尝试了利用css 但是对于安卓没效果ios试了可以

 -webkit-user-select: none; /*Safari */
 -ms-user-select: none; /*IE 10+ and Edge */
 -o-user-select: none;
 -moz-user-select: none; /*火狐*/
 -khtml-user-select: none; /*早期浏览器*/
 user-select: none; /*Standard syntax*/
 -webkit-touch-callout: none;

(2)、尝试将文字换成图片 结果会出现保存图片的弹窗,因此最终选择了  event.preventDefault()

3、松开结束并发送录音

// 结束录制语音
function stopAndSendRecording(event) {
  if (mediaRecorder.value) {
    mediaRecorder.value.addEventListener('stop', () => {
      audioBlob.value = new Blob(audioChunks.value, { type: 'audio/mp3' })
      posEnd.value = JSON.stringify(event) === '{}' ? posStart.value : event.changedTouches[0].pageY// 获取终点坐标
      if (posStart.value - posEnd.value < 100) {
        // 判断语音时长
        if (min.value === 0 && sec.value <= 1) {
          resetTimer()
          showToast({
            message: '说话时间太短',
            icon: warnPng,
            duration: 3000,
            zIndex: 2026,
          })
        }
        else {
          sendAudio(min.value * 60 + sec.value) //发送语音
          resetTimer()
        }
      }
      // 释放麦克风权限
      audioStream.value.getAudioTracks().forEach((track) => {
        track.stop()
      })
    })
    mediaRecorder.value.stop()
    mediaRecorder.value = null
  }
}

3.1语音发送

调用语音发送是遇到一个问题,传参数给后端时file传formData时,传值是对象而并非是二进制文件流(请求体如下),最终通过FormData的get方法获取到了file文件,解决了这个问题

落地代码如下:

// 语音发送
function sendAudio(duration) {
  const formData = new FormData()
  formData.append('file', audioBlob.value, 'audio.mp3')
  uploadGuideFile({ file: formData.get('file'), groupKey: 'base-audio', uploadPlatform: 1, uploadSource: 1 }).then((res) => {
    // console.log('上传语音', res)
    const data = {
      baseFileId: res.data.id,
      audioTotalDuration: duration,
    }
    loadingVoice.value = false
    emits('sendGuideEv', data)
  })
}

4、三分钟自动发送语音相关

// 计时器三分钟
function createTimer() {
  if (timer.value) {
    clearInterval(timer.value)
  }
  timer.value = setInterval(() => {
    sec.value += 1
    if (sec.value === 60) {
      min.value += 1
      sec.value = 0
    }
    if (min.value === 3) {
      // 三分钟自动发送
      stopAndSendRecording({})
    }
    timeNum.value = `${(`0${min.value}`).slice(-2)}:${(`0${sec.value}`).slice(-2)}`
  }, 1000)
}
// 清除计时器重置语音时长
function resetTimer() {
  clearInterval(timer.value)
  min.value = 0
  sec.value = 0
  timeNum.value = `${(`0${min.value}`).slice(-2)}:${(`0${sec.value}`).slice(-2)}`
}

三、播放语音

播放语音最初采用方案是利用H5原生new Audio创建audio播放器,但是测试过程中出现:在oppo默认浏览器中,语音无法播放的问题。页面中写了固定audio标签,也不能正常播放音频。此方案处理音频具有兼容性问题。因此最终选择Howler.js 库。

// 语音播放
function playVoice(item) {
  if (activeAudio.value === item.id) { // 点击正在播放的语音
    audio.value.stop()
    audio.value = null
    activeAudio.value = 0
  }
  else {
    if (audio.value) { // 有播放点其他的音频
      audio.value.stop()
      audio.value = null
    }
    activeAudio.value = item.id
    const audioUrl = `${import.meta.env.VITE_APP_FILE}${item.audioPath}`
    audio.value = new Howl({
      src: [audioUrl], // 提供多个格式以提高兼容性
      autoplay: true, // 是否自动播放,默认为false
      loop: false, // 是否循环播放,默认为false
      volume: 0.5, // 音量大小,范围是0-1,默认为1
      preload: true, // 是否预加载音频,默认为true
    })
    audio.value.play()
    audio.value.on('end', () => {
      window.console.log('音频播放结束')
      audio.value = null
      activeAudio.value = 0
    })
  }
}

### Window.Navigator.MediaDevices API 使用方法 #### 请求媒体设备权限并处理流数据 为了访问用户的多媒体输入设备(如摄像头和麦克风),可以使用 `navigator.mediaDevices.getUserMedia()` 方法。此方法接收一个约束条件对象作为参数,该对象指定了所需的音视频特性。 ```javascript const constraints = { audio: true, video: { width: 1280, height: 720 } }; async function startUserMedia() { try { const stream = await navigator.mediaDevices.getUserMedia(constraints); handleSuccess(stream); } catch (err) { console.error("Error accessing media devices.", err); } } function handleSuccess(stream) { const videoElement = document.getElementById('localVideo'); videoElement.srcObject = stream; } ``` 上述代码展示了如何定义媒体约束并尝试获取相应的媒体流[^1]。 #### 屏幕共享功能实现 对于屏幕共享的需求,则应采用 `navigator.mediaDevices.getDisplayMedia()` 方法。它同样需要传入一个描述所需资源类型的选项对象: ```javascript async function startScreenShare() { try { const screenStream = await navigator.mediaDevices.getDisplayMedia({ video: true, audio: false // 可选开启音频捕获 }); attachMediaStream(screenStream); // 自定义函数用于显示或操作获得的流 } catch (error) { console.log(`getUserMedia error: ${error}`); } } ``` 这段脚本说明了怎样启动屏幕分享流程,并且可以选择是否同时收集声音信息[^2]。 #### 处理旧版浏览器兼容性问题 考虑到不同版本浏览器的支持情况,在实际开发过程中可能还需要考虑向后兼容的问题。可以通过检测是否存在特定的方法名来进行适配: ```javascript if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { alert('Your browser does not support the latest WebRTC features.'); } else { // 继续执行其他逻辑... } ``` 这种做法能够帮助开发者更好地应对各种环境下的应用部署需求[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值