项目背景
公司业务为分享链接给到用户,用户进入上传照片,视频等,需直接调用相机, 不允许从相册里拿,并对拍摄出视频大小有要求,为此进行技术调研
进程
一、Media Recorder API
因从未参与过此类的技术开发,两眼一抹黑,像无头苍蝇一样在搜索引擎里查找,首先找到了 Media Recorder API Web API。在此方案上找了N多的Demo进行验证
// 开始录制
function startRecording(stream, lengthInMS) {
recorder = new MediaRecorder(stream, {
//因为视频和音频分开录制这里直接注释了
// audioBitsPerSecond: 128000, // 音频码率
bitsPerSecond: 1843200, // 视频码率
mimeType: "video/webm", // 编码格式
})
recorder.ondataavailable = (event) => {
let data = event.data;
dataChunks.push(data);
};
recorder.start(1000);
console.log(recorder.state + " start to recording .....");
}
//访问用户媒体设备的兼容方法
const getUserMedia = (constraints, success, error) => {
if (navigator.mediaDevices.getUserMedia) {
//最新的标准API
navigator.mediaDevices.getUserMedia(constraints).then(success).catch(error);
var supportedConstraints = navigator.mediaDevices.getSupportedConstraints();
console.log('supportedConstraints', supportedConstraints)
} else if (navigator.webkitGetUserMedia) {
//webkit核心浏览器
navigator.webkitGetUserMedia(constraints, success, error)
} else if (navigator.mozGetUserMedia) {
//firfox浏览器
navigator.mozGetUserMedia(constraints, success, error);
} else if (navigator.getUserMedia) {
//旧版API
navigator.getUserMedia(constraints, success, error);
}
}
const Start = () => {
if (navigator.mediaDevices.getUserMedia || navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia) {
startTime.value = new Date().getTime()
//调用用户媒体设备, 访问摄像头
getUserMedia({
video: {
width: { ideal: 1280, max: 1280 },
height: { ideal: 720, max: 720 },
facingMode: { exact: "environment" }, // 设置前后摄像头
frameRate: { ideal: 30, max: 30 }, // 设置帧数
torch: true
}
}, success, error);
} else {
alert('不支持访问用户媒体');
}
}
优点:
API 可以设置拍摄时的分辨率,帧数等参数,控制拍摄出视频大小
缺点:
1、支持的浏览器数量方面不足,测试5个手机3个不行(Can I use)
2、拍摄出视频没有时间属性,导致播放时没有进度条
解决视频没有时间属性,可以使用 EBML.js 解决
function getSeekableBlob(inputBlob, callback) {
// EBML.js copyrights goes to: https://github.com/legokichi/ts-ebml
if (typeof EBML === 'undefined') {
throw new Error('Please link: https://www.webrtc-experiment.com/EBML.js');
}
const reader = new EBML.Reader();
const decoder = new EBML.Decoder();
const tools = EBML.tools;
const fileReader = new FileReader();
fileReader.onload = function (e) {
const ebmlElms = decoder.decode(this.result);
ebmlElms.forEach((element) => {
reader.read(element);
});
reader.stop();
const refinedMetadataBuf = tools.makeMetadataSeekable(reader.metadatas, reader.duration, reader.cues);
const body = this.result.slice(reader.metadataSize);
const newBlob = new Blob([refinedMetadataBuf, body], {
type: 'video/webm'
});
callback(newBlob);
};
fileReader.readAsArrayBuffer(inputBlob);
}
此方案Demo代码 video-web-record-api-demo 注意受浏览器安全限制,只有localhost或者https的网站才支持录制屏幕,上线需要部署https
二、HTML Media Capture
后续查到可以在手机端浏览器中使用HTML Media Capture来稳定的录制视频
<div><input type="file" accept="video/*" capture="user">video</div>
但是不能设置分辨率,帧数等参数,所以HTML Media Capture录制产物较大,所以又寻找压缩视频的方案,找到 FFmpeg.wasm 库可以在前端压缩视频
const { createFFmpeg, fetchFile } = FFmpeg;
let ffmpeg = null;
const transcode = async ({ target: { files } }) => {
if (ffmpeg === null) {
ffmpeg = createFFmpeg({ log: true });
}
const message = document.getElementById('message');
const { name } = files[0];
message.innerHTML = 'Loading ffmpeg-core.js';
if (!ffmpeg.isLoaded()) {
await ffmpeg.load();
}
console.log('transcode 初始大小', ((files[0]).size / 1024 / 1024).toFixed(2), 'M')
console.time('transcode')
ffmpeg.FS('writeFile', name, await fetchFile(files[0]));
message.innerHTML = 'Start transcoding';
// await ffmpeg.run('-i', name, 'output.mp4');
await ffmpeg.run('-i', name, '-c:v', 'libx264', '-crf', '23', '-preset', 'medium', '-c:a', 'aac', '-b:a', '128k', '-movflags', '+faststart', '-vf', 'scale=-2:720,format=yuv420p', 'output.mp4');
// await ffmpeg.run('-y', '-i', name, '-s', '1280x720', '-r', '5', '-c:v', 'libx264', '-b:v', '600k', '-b:a', '44100', '-ac', '2', '-ar', '22050', '-tune', 'fastdecode', '-preset', 'ultrafast', 'output.mp4')
message.innerHTML = 'Complete transcoding';
const data = ffmpeg.FS('readFile', 'output.mp4');
console.timeEnd('transcode')
console.log('transcode 压缩后大小', ((new Blob([data.buffer])).size / 1024 / 1024).toFixed(2), 'M')
const video = document.getElementById('output-video');
video.src = URL.createObjectURL(new Blob([data.buffer], { type: 'video/mp4' }));
}
const elm = document.getElementById('uploader');
elm.addEventListener('change', transcode);
const cancel = () => {
try {
ffmpeg.exit();
} catch(e) {}
ffmpeg = null;
}
压缩视频时间长,1分钟视频的压缩时间在1~2分钟,业务不接受,并且 FFmpeg.wasm 包大小在8M,致使页面加载时间较长,用户体验全无
三、WebRTC
WebRTC是一个免费、开放的项目。使web浏览器通过简单的JavaScript api接口实现实时通信功能。 还有另外一个重要的优势:因为它是以数据流方式工作的,所以它没有上传时间,并且在浏览器崩溃的情况中不会造成数据丢失。 但是WebRTC技术复杂,基础设备:信令服务器,TURN/STUN服务器,WebRTC终端,连接过程:连接过程可能在信令,发现或者P2P连接的时候发生失败
四、微信小程序
微信小程序 camera 组件配合 CameraContext API 实现视频拍摄
<camera binderror="getError" resolution="medium" device-position="{{devicePosition}}" style="width: 100vw;height: 100vh;"></camera>
this.data.ctx.stopRecord({
// compressed: false, //是否压缩录完的视频
success: (res) => {
console.log(res)
clearInterval(this.data.timer)
this.setData({
video_url: res.tempVideoPath,
isRecording: false
})
this.compressVideo(res.tempVideoPath)
this.data.isStop = false
wx.hideLoading()
},
fail(err) {
console.log(err)
this.data.isStop = false
wx.hideLoading()
}
})
compressVideo(path) {
console.time('compressVideo')
let compressVideoTime = new Date().getTime()
console.log('开始压缩', path)
wx.compressVideo({
src: path,
bitrate: 900,
fps: 30,
resolution: 1,
success: (res) => {
console.log('压缩完成', res.tempFilePath, 'Time', (new Date().getTime()) - compressVideoTime, 'ms')
console.timeEnd('compressVideo')
let size = res.size / 1024;
console.log('compressVideo Size:', size.toFixed(2), 'M')
this.setData({
video_url_compress: res.tempFilePath
})
},
fail: (err) => {
console.log('err', err)
}
})
},
微信小程序,提供拍摄时的参数设置,和视频压缩,视频压缩的速度较快,产物根据配置参数控制其大小,并且微信为我们趟平了兼容性的问题,可满足业务需要
参考
在HTML5视频录制方面,我们为什么选WebRTC而不选Media Recorder API
RecordRTC网页录制视频无法播放以及无时长问题
微信小程序文档