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

一、需求功能点描述

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

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

二、开发过程

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
    })
  }
}

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
WebRTC(Web Real-Time Communication)是一种基于网页的实时通信技术,可以通过浏览器实现音频、视频和数据的实时传输。在H5实现语音通话,可以使用WebRTC来实现。 首先,确保你的浏览器支持WebRTC,大多数现代浏览器都已经支持了。然后,你可以通过以下步骤来实现语音通话: 1. 获取用户媒体设备访问权限:通过调用`navigator.mediaDevices.getUserMedia()`方法来请求用户的麦克风权限,这将返回一个包含音频流的Promise对象。 2. 创建本地音频流:获取到用户的麦克风权限后,可以使用`MediaStream`对象来表示音频流。可以通过`new MediaStream()`方法创建一个空的音频流对象,并通过`addTrack()`方法将用户的音频轨道添加到音频流中。 3. 建立连接:使用WebRTC中的`RTCPeerConnection`对象来建立连接。创建一个新的`RTCPeerConnection`对象,并通过`addTrack()`方法将本地音频流添加到连接中。 4. 建立信令通道:为了在通话双方之间交换信息,需要建立一个信令通道。你可以使用WebSocket、Socket.io或其他实时通信协议来实现。通过信令通道,你可以交换ICE候选者、会话描述等信息。 5. 发送和接收音频流:一旦连接建立并且信令通道准备好,你可以使用`RTCPeerConnection`对象的`createOffer()`方法创建一个会话描述,然后通过信令通道发送给对方。对方收到会话描述后,可以使用`setRemoteDescription()`方法设置远程描述,并调用`createAnswer()`方法创建自己的会话描述,并发送给你。通过交换会话描述,双方就可以建立起音频通话。 6. 通过ICE候选者建立对等连接:在建立连接过程中,`RTCPeerConnection`对象会自动收集ICE(Interactive Connectivity Establishment)候选者,用于建立对等连接。ICE候选者包含了网络地址信息,可以通过信令通道交换给对方。 7. 建立音频流传输:一旦对等连接建立,你可以通过监听`RTCPeerConnection`对象的`ontrack`事件来获取对方的音频流。这样就可以播放对方的音频,并通过`RTCPeerConnection`对象的`addTrack()`方法将对方的音频轨道添加到连接中。 8. 关闭连接:通话结束后,记得关闭连接和释放资源。调用`RTCPeerConnection`对象的`close()`方法来关闭连接,并通过调用`stream.getTracks().forEach(track => track.stop())`方法停止本地音频流的传输。 这只是一个简单的概述,实现WebRTC语音通话涉及到更多细节和技术细节。你可以参考WebRTC的官方文档和示例代码,深入了解和实践WebRTC的语音通话功能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值