本文主要实现了基于 Electron 技术的屏幕录制、麦克风声音录制、摄像头(画中画)录制。
整体效果如下所示:
代码中,比较重要的就是对多个媒体轨道进行合成、视频合成。
核心代码如下:
// This file is required by the index.html file and will
// be executed in the renderer process for that window.
// No Node.js APIs are available in this process because
// `nodeIntegration` is turned off. Use `preload.js` to
// selectively enable features needed in the rendering
// process.
const jQuery = (window.jQuery = require('jquery'));
require('@popperjs/core');
require('bootstrap');
const Vue = require('vue/dist/vue');
const electron = require('electron');
const fs = require('fs');
const SCREEN_WIDTH = 3072;
const SCREEN_HEIGHT = 1920;
const PlayerCanvas = require('./PlayerCanvas');
new Vue({
el: '#vueapp',
data: {
recording: false,
},
mounted() {
this._playerCanvas = new PlayerCanvas(SCREEN_WIDTH, SCREEN_HEIGHT);
},
methods: {
// 开始录制
async btnStartRecordClicked(e) {
this._stream = new MediaStream();
await this.attachAudioStream();
// 摄像头 stream
this._cameraStream = await navigator.mediaDevices.getUserMedia({
video: true,
audio: false,
});
this._playerCanvas.setCameraVideo(
this.createVideoElementWithStream(this._cameraStream)
);
// 屏幕 stream
this._screenStream = await navigator.mediaDevices.getUserMedia({
audio: false,
video: {
mandatory: {
chromeMediaSource: 'desktop',
minWidth: SCREEN_WIDTH,
maxWidth: SCREEN_WIDTH,
minHeight: SCREEN_HEIGHT,
maxHeight: SCREEN_HEIGHT,
},
},
});
this._playerCanvas.setScreenVideo(
this.createVideoElementWithStream(this._screenStream)
);
//
this._audioStream
.getAudioTracks()
.forEach((value) => this._stream.addTrack(value));
let playerCanvasStream = this._playerCanvas.canvas.captureStream();
playerCanvasStream.getTracks().forEach((t) => this._stream.addTrack(t));
this.$refs.preview.srcObject = playerCanvasStream;
this.startRecord();
},
// 附加音频流
async attachAudioStream() {
// 获取麦克风流
this._audioStream = await navigator.mediaDevices.getUserMedia({
video: false,
audio: true,
});
// 将麦克风的流,附加到主流上
this._audioStream
.getAudioTracks()
.forEach((value) => this._stream.addTrack(value));
},
// 停止录制
btnStopRecordClicked(e) {
this.recording = false;
this._recorder.stop();
},
// 创建一个 HTMLVideoElement
createVideoElementWithStream(stream) {
let video = document.createElement('video');
video.autoplay = true;
video.srcObject = stream;
return video;
},
// 开始录制
startRecord() {
this._recorder = new MediaRecorder(this._stream, {
mimeType: 'video/webm;codes=h264',
});
this._recorder.ondataavailable = async (e) => {
let path = electron.remote.dialog.showSaveDialogSync(
electron.remote.getCurrentWindow(),
{
title: '保存文件',
defaultPath: 'ScreenData.webm',
}
);
let dataArrayBuffer = await e.data.arrayBuffer();
fs.writeFileSync(path, Buffer.from(dataArrayBuffer));
// fs.writeFileSync(path, new Uint8Array(dataArrayBuffer));
};
this._recorder.start();
this.recording = true;
},
},
});
完整源代码下载: