Vue调用摄像头录制视频和音频并上传给后端或下载到本地

下载插件

npm install --save webm-duration-fix

代码及作用

调用摄像头

callCamera () {

let _this = this;

MediaUtils.getUserMedia(true, true, function (err, stream) {

if (err) {

throw err;

} else {

// 通过 MediaRecorder 记录获取到的媒体流

const mimeType = 'video/webm;codecs=vp8,opus';

mediaRecorder = new MediaRecorder(stream, {

// mimeType: "video/webm;codecs=vp9",

mimeType: mimeType,

});

mediaStream = stream;

var chunks = []

var video = _this.$refs.videos;

video["srcObject"] = stream;

video.play();// 播放实时画面

mediaRecorder.ondataavailable = function (e) {

mediaRecorder.blobs.push(e.data);

chunks.push(e.data);

};

mediaRecorder.blobs = [];

mediaRecorder.onstop = async () => {

recorderFile = await fixWebmDuration(new Blob(chunks, { type: mimeType }));

console.log(recorderFile);

var url = URL.createObjectURL(recorderFile)

var videosreplay = _this.$refs.videosreplay;

videosreplay.setAttribute("src", url);

console.log('url', url)

chunks = [];

if (null != stopRecordCallback) {

stopRecordCallback();

}

};

_this.record()

}

});

},

开始结束录制

record () {

if (this.recordtype == "ING") {

this.stopRecord(() => {

console.log("结束录制");

this.toggleReplayVideo()

});

}

else if (this.recordtype == "BEGIN") {

console.log("开始录制");

this.startAudio();

mediaRecorder.start();

startTime = Date.now();

this.recordtype = "ING";

}

},

对录像时长进行记录

startAudio () {

this.timer = setInterval(() => {

this.recordtime += 1000;

if (this.recordtime == 1000000) {

this.stopRecord();

}

this.second++;

if (this.second >= 60) {

this.second = 0;

this.minute = this.minute + 1;

}

if (this.minute >= 60) {

this.minute = 0;

this.hour = this.hour + 1;

}

console.log(this.recordtime)

}, 1000);

},

停止录像时终止录制器,关闭媒体流并清除时长记录定时器

stopRecord (callback) {

this.recordtype = "END";

this.showReplay = true;

stopRecordCallback = callback;

clearInterval(this.timer);

// 终止录制器

mediaRecorder.stop();

// 关闭媒体流

MediaUtils.closeStream(mediaStream);

var videosreplay = this.$refs.videosreplay;

videosreplay.onended = () => {

this.playtime = 0;

this.replayVideo = false;

clearInterval(this.playtimer);

};

videosreplay.onclick = () => {

this.showReplay = !this.showReplay;

};

},

回放

toggleReplayVideo () {

console.log('播放中...')

this.replayVideo = !this.replayVideo;

this.showReplay = false;

var videosreplay = this.$refs.videosreplay;

if (this.replayVideo) {

videosreplay.play().catch(err => {

this.$message.error(err.message);

console.log(err);

});

this.playtimer = setInterval(() => {

this.playtime += 1000;

}, 1000);

} else {

videosreplay.pause();

clearInterval(this.playtimer);

}

},

下载视频

指定且只能指定,下载后的默认文件名字和文件后缀。注意,可以不指定后缀名,浏览器会根据数据类型自动为其匹配后缀名,但是最好指定后缀。

SAVE

下载后的文件名为after.jpg download属性不能指定下载路径; 当 download 属性值为空时,下载的文件的名字和扩展名与源文件一致;当href为base64编码的图像数据时,则下载后文件名也是那么离谱得长。

SAVE

下载后的文件名为data_image_jpeg;base64,… .jpg

download () {

var url = URL.createObjectURL(recorderFile)

console.log("URLLLLLL", url)

const a = document.createElement("a");

document.body.appendChild(a);

a.style.display = "none";

a.href = url;

if (this.fileName) {

a.download = this.fileName + ".mp4";

} else {

a.download = new Date() + ".mp4";

}

a.click();

window.URL.revokeObjectURL(url);

},

下载或上传给后端

submit () {

let that = this;

console.log(recorderFile)

// 下载

this.download()

let file = new File(

[recorderFile],

"msr-" + new Date().toISOString().replace(/:|\./g, "-") + ".mp4",

{

type: "video/mp4",

}

);

let config = {

headers: { "Content-Type": "multipart/form-data" }

}

console.log('file', file)

const formdata = new FormData()

formdata.append("file", file);

// 传给后端

// axios.post('/video', formdata, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } },) //请求头要为表单

// .then(response => {

// console.log('video', response.data);

// this.yy_score = parseInt(response.data.data + 0.5)

// that.progress = response.data.data * 1.0 / 23 * 100

// })

// .catch(function (error) {

// that.$message({

// message: error,

// type: 'error'

// });

// console.log(error);

// })

},

var MediaUtils = {

/**

* 获取用户媒体设备(处理兼容的问题)

* @param videoEnable {boolean} - 是否启用摄像头

* @param audioEnable {boolean} - 是否启用麦克风

* @param callback {Function} - 处理回调

*/

getUserMedia: function (videoEnable, audioEnable, callback) {

navigator.getUserMedia =

navigator.getUserMedia ||

navigator.webkitGetUserMedia ||

navigator.mozGetUserMedia ||

navigator.msGetUserMedia ||

window.getUserMedia;

var constraints = { video: videoEnable, audio: audioEnable };

if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {

navigator.mediaDevices

.getUserMedia(constraints)

.then(function (stream) {

callback(false, stream);

})

["catch"](function (err) {

callback(err);

});

} else if (navigator.getUserMedia) {

navigator.getUserMedia(

constraints,

function (stream) {

callback(false, stream);

},

function (err) {

callback(err);

}

);

} else {

callback(new Error("Not support userMedia"));

}

},

/**

* 关闭媒体流

* @param stream {MediaStream} - 需要关闭的流

*/

closeStream: function (stream) {

if (typeof stream.stop === "function") {

stream.stop();

} else {

let trackList = [stream.getAudioTracks(), stream.getVideoTracks()];

for (let i = 0; i < trackList.length; i++) {

let tracks = trackList[i];

if (tracks && tracks.length > 0) {

for (let j = 0; j < tracks.length; j++) {

let track = tracks[j];

if (typeof track.stop === "function") {

track.stop();

}

}

}

}

}

},

};

var startTime, mediaRecorder, mediaStream, stopRecordCallback, recorderFile;

页面完整代码

结果

录制

播放

下载

项目代码

https://gitee.com/yuan-hongting/video

[an error occurred while processing the directive]