安卓APP音视频录制与播放
原创大约 3 分钟
前言
本项目是一个基于hatom平台开发的安卓app(H5内嵌webview),本文总结归纳了其中音视频录制与播放开发业务的一些经验。
音频录制
由于处在安卓webview环境,录制音频需要调起硬件设备,并且因为网页浏览器有安全机制,它只允许https和localhost访问麦克风摄像头等硬件,通过H5录制音频(MediaDevices.getUserMedia())的方式不太友好。固采用hatom平台提供的原生音频录制插件进行录制,但是原生录制插件无法提供录制时间,所以这里采用手动计时,相关代码如下:
长按开始录制
startVoice() {
this.timeOutEvent && clearTimeout(this.timeOutEvent)
this.timeOutEvent = setTimeout(() => {
this.$hatom.native('VideoRecordPlugin.startAudioRecord', res => {
// 开始计算录音时间,只记录到秒
if (res.code === 0) {
this.startRecord = true
this.recordTime = 0
this.startTime = dayjs()
this.updateTime()
}
})
}, 500)
}
记录时间
updateTime() {
if (this.startRecord) {
this.recordTime = dayjs().diff(this.startTime, 's')
if (this.recordTime >= 30) {
this.stopVoice()
return
}
requestAnimationFrame(this.updateTime)
}
}
结束录制
stopVoice() {
this.startRecord &&
this.$hatom.native('VideoRecordPlugin.stopAudioRecord', async res => {
if(this.recordTime < 1) {
Toast.fail('录音时间太短')
this.init()
return
}
const filePath = await API.upload([JSON.parse(res.message).filePath], {}, '录音保存中', '保存成功', '保存失败')
this.$emit('stopVoice', {
filePath: filePath[0].src?.includes('http') ? filePath[0]?.src : `${this.GLOBAL?.address}${filePath[0].src}`,
time: this.recordTime
})
this.showPopup = false
})
this.startRecord = false
clearTimeout(this.timeOutEvent)
},
音频播放
语音播放采用H5的audio标签进行播放,不需要利用原生的能力。
开始播放
调用audio的play方法进行播放,由于存在倒计时,所以需要等待语音文件加载完成后才能进行播放,否则会存在网络延迟问题,导致时间对应不上。
playVoice() {
this.needFirstPlay = true //点击时正在加载中,加载完成自动播放的标志
if (!this.$refs.audio.src) {
this.$refs.audio.src = this.voiceFile
}
if (this.isError) {
Toast.fail('语音加载失败')
return
}
if (!this.isReady) {
this.firstPlayLoading = Toast.loading({
message: '语音加载中',
forbidClick: true,
duration: 0
})
return
}
//正在播放,点击时就停止播放,否则就开始播放
if (this.isPlayVoice) {
this.stopPlayVoice()
} else {
this.isPlayVoice = true
// 显示倒计时
this.currentTime = Date.now() + this.voiceTime * 1000
this.countDown()
if (this.isOnlineFile) {
this.$refs.audio.play()
}
}
}
判断语音是否加载完成可以监听audio的canplaythrough的回调方法。
audioReady() {
this.isReady = true
if (this.needFirstPlay) {
this.firstPlayLoading && this.firstPlayLoading.clear()
this.playVoice()
}
}
倒计时
countDown() {
const remainingTime = this.currentTime - Date.now()
this.playTime = Math.ceil(remainingTime / 1000)
if (this.playTime <= 0) {
this.stopPlayVoice()
} else {
this.animationId = requestAnimationFrame(this.countDown)
}
}
停止播放
调用audio的pause方法
stopPlayVoice() {
this.isPlayVoice = false
this.needFirstPlay = false
this.playTime = this.voiceTime
cancelAnimationFrame(this.animationId)
this.$refs.audio.pause()
this.$refs.audio.currentTime = 0
},
视频录制
视频录制同样可以利用hatom平台提供的原生插件能力,本项目app暂时没有视频录制需求,使用请看官方插件文档
视频播放
视频播放可以采用H5的video标签进行播放,这里推荐一个十分好用的第三方库video.js,中文文档。可以自定义video播放器样式等等。
QA
1、视频播放器配置 language: 'zh-CN'无法生效
//引入中文json文件,并配置语言
import lang_zhCn from 'video.js/dist/lang/zh-CN.json'
videoJs.addLanguage('zh-CN', lang_zhCn)
2、关于播放器controlBar自定义配置(官方文档缺少)
3、显示首帧
有两种方案。
一是通过canvas截取首帧,并输出为url,这种方案需要服务器支持,需要解决canvas跨域CORS问题
getFirstImage() {
const canvas = document.createElement('canvas')
canvas.width = this.videoInstance.currentWidth()
canvas.height = this.videoInstance.currentHeight()
const context = canvas.getContext('2d')
const el = this.videoInstance.el().querySelector('video')
context.drawImage(el, 0, 0, canvas.width, canvas.height)
this.imageSrc = canvas.toDataURL('image/jpeg')
},
二是监听视频的loadeddata(视频加载完首帧)的回调。执行play方法,同时立马pause视频
// 获取首帧
this.videoInstance.on('loadeddata', async () => {
await this.videoInstance.play()
await this.videoInstance.pause()
})
4、进度条无法拖动
视频拖动进度条需要视频服务器支持返回头
Accept-Ranges: bytes
Content-Length: xxx
Content-Range: bytes xxxx