如题,今天我们用纯web技术,实现摄像头+麦克风 视频的录制功能,代码约100余行, 主要涉及的知识点:
-
MediaDevices[1]
提供对连接的媒体输入设备(如照相机和麦克风)的访问,以及屏幕共享等。 -
MediaRecorder[2]
录制音频或者视频。 -
IndexedDB[3]
储存较大数据结构的事务性数据库。 -
URL[4]
用来把视频的Blob数据生成地址,提供给video
标签使用。
效果演示
真机效果

源码地址
本文源码-recordAV[5]
注意:
-
权限问题,需要显式的授权 -
如果手机端预览,需要启用https,demo已经附带证书
思路
-
利用MediaDevices唤起摄像头和麦克风 -
把第一步获取的流,同时用于 video
和MediaRecorder
因为录制的同时需要看到我们摄像头的内容 -
录制结束后,把录制视频存入indexedDB -
按照keys列出已录制的视频,点击后,获取Blob文件,生成url,提供给video标签播放。
实现
唤起摄像头和麦克风并获得其流
这里需要用的就是MediaDevices[6],对应的API就是navigator.mediaDevices.getUserMedia
核心代码如下:
const stream = await navigator.mediaDevices.getUserMedia({
video: { facingMode: "environment" }, // 唤起内面的摄像头,
audio: true // 需要音频,例如麦克风
})
// 把流传给video元素,即可看到摄像头内容
videoEL.srcObject = stream;
// 初始化 MediaRecorder
mediaRecorder = new MediaRecorder(stream, { mimeType: "video/webm" });
注意事项:
-
getUserMedia方法的facingMode参数 user
为前置的摄像头,environment
为后置摄像头。 -
new MediaRecorder的参数 { mimeType: "video/webm" }
如果未设置正确,可能就只有视频,没有麦克风声音了
录制和保存
录制必然有开始和停止两个操作,实现方式很多,我们就采用最简单的两个按钮形式, 并分别给注册上相关的事件处理程序。
代码如下:
这里有一个小的知识点,任何有id属性的节点,你均可使用id属性对应的变量直接访问该元素。
<button id="btnRecord" class="btn">录制</button>
<button id="btnStop" class="btn" >停止</button>
btnRecord.addEventListener("click", () => {
startRecord(mediaRecorder);
mediaRecorder.start();
});
btnStop.addEventListener("click", () => {
mediaRecorder.stop();
})
是不是很简单。注意上面停止按钮点击后,我们调用了mediaRecorder.stop
方法,其之后会触发recorder.onstop
事件,这个时候,我们唤起弹出框,让用户输入视频的名字,然后将内容保存到indexedDB即可。
indexedDB的存取有很多封装库,indexedDB 参见[7]部分列出了不少于10个库,这里我们采用 idb-keyval[8]库,其简单且小巧的(~600B)基于 Promise 的键值对存储,使用也是极其简单,get, set就行了。
具备上面的知识后,看代码:是不是很简单。
function startRecord(recorder) {
var chunks = [];
// 收集数据
recorder.ondataavailable = function (e) {
chunks.push(e.data);
}
// 监听停止事件
recorder.onstop = async () => {
var clipName = prompt('请输入视频的名字');
var blob = new Blob(chunks, { 'type': 'audio/mp4;' });
await idbKeyval.set(clipName + ".mp4", blob);
listHistory();
}
}
历史和观看
历史嘛,那就是读取keys,严格意义上,应该使用indexedDB的游标来读取,本文为了简单,直接读取所有的keys,然后判断文件后缀来过滤。
async function listHistory() {
list.innerHTML = null;
const keys = await idbKeyval.keys();
console.log("keys:", keys);
keys.filter(k => k.endsWith(".mp4")).forEach(key => {
const divEl = document.createElement("div");
divEl.textContent = key;
divEl.onclick = () => playVideo(key);
list.appendChild(divEl);
});
}
到此为止,我们就差点击某个历史视频之后的播放逻辑了,也很简单:
async function playVideo(key) {
const blob = await idbKeyval.get(key);
// 生成地址
fplayer.src = URL.createObjectURL(blob);
fplayer.style.display = "block";
fplayer.play();
}
到此文本,所有的核心代码都已经实现了。
写在最后
如果你觉得不错,你的一赞一评就是我前行的最大动力。
技术交流
或者添加我的微信 dirge-cloud,一起交流学习。
MediaDevices.getUserMedia()的部分坑和解决方案[10]
参考资料
MediaDevices: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
[2]
MediaRecorder: https://developer.mozilla.org/en-US/docs/Web/API/MediaRecorder
[3]
IndexedDB: https://developer.mozilla.org/en-US/docs/Glossary/IndexedDB
[4]
URL: https://developer.mozilla.org/zh-CN/docs/Web/API/URL
[5]
本文源码-recordAV: https://github.com/xiangwenhu/juejinBlogsCodes/tree/master/recordAV
[6]
MediaDevices: https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices
[7]
indexedDB 参见: https://developer.mozilla.org/zh-CN/docs/Web/API/IndexedDB_API#%E5%8F%82%E8%A7%81
[8]
idb-keyval: https://www.npmjs.com/package/idb-keyval
[9]
https://juejin.cn/pin/6994350401550024741: https://juejin.cn/pin/6994350401550024741
[10]
MediaDevices.getUserMedia()的部分坑和解决方案: https://blog.csdn.net/weixin_43864427/article/details/105782611
原文始发于微信公众号(成长的程序世界):100余行代码,纯web技术一起实现摄像头和麦克风视频录制,并带历史记录功能
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/268017.html