前言
在上一期咱们运用Electron
完结了一个自己的录屏软件,评论区有小伙伴说怎么支撑区域录制。固然区域录制也是一个非常常见的需求,所以今天咱们就来完结一下录屏软件的区域录制。
上一期的文章链接在这,感兴趣的朋友们能够前往观看:Electron打造你自己的录屏软件
前置优化
在开端区域录制之前,先优化一下之前的一些完结方式,以及从头界说一些概念。便利咱们更好地开端完结后边的逻辑。
从头界说清晰度
在上一期中咱们界说了几种清晰度标准:
- 超清:
3840x2160
分辨率 - 高清:
1280x720
分辨率 - 标清:
720x480
分辨率
然后经过ffmpeg
指定分辨率去录制不同质量的视频,但其实这样的完结是有一些问题的。试想一下你的录制硬件设备最高的分辨率都没达到超清的分辨率,那么设置超清的分辨率也是没有意义的。
所以录制的时分我会不指定分辨率,这样ffmpeg
就会采用最高分辨率来录制,录制完结之后再把视频转成指定的分辨率。这儿的分辨率我也从头界说了一下,现在是以百分比来设置。
const DEFINITION_LIST = [
{ label: '100%', value: '1' },
{ label: '75%', value: '0.75' },
{ label: '50%', value: '0.5' },
{ label: '25%', value: '0.25' }
]
录制命令就改成了下面的姿态
const ffmpegCommand = `${ffmpegPath} -f avfoundation -r ${frameRate} -i "1" -c:v libx264 -preset ultrafast ${fileName}`
视频格式转化
录制完结之后,再调用ffmpeg
的视频格式转化功用,这个时分顺便把分辨率一起设置一下。录制完结后对应的子进程就会退出,此时监听子进程的退出事情,调用转码方法。
ffmpegProcess.on('exit', (code, signal) => {
console.log(`Recording process exited with code ${code} and signal ${signal}`)
afterRecord()
})
下面是afterRecord
的部分完结,解释一下完结流程:
-
fileName
是录制的默许mp4
文件,output
是咱们要输出的视频文件 - 根据挑选导出的视频格式,将
mp4
文件转成对应的格式 - 启动一个子进程,执行
ffmpeg
命令处理视频 - 视频处理完结后删除默许
mp4
文件,并翻开保存视频的文件夹
const output = `${FILE_PATH}/record-${moment().format('YYYYMMDDHHmmss')}.${ext}`
if (ext === 'mp4') {
command = `${ffmpegPath} -i ${fileName} -vf "scale=${scale}" -c:a copy ${output}`
} else if (ext === 'webm') {
command = `${ffmpegPath} -i ${fileName} -vf "scale=${scale}" -c:v libvpx -c:a libvorbis ${output}`
} else if (ext === 'gif') {
command = `${ffmpegPath} -i ${fileName} -vf "fps=15,scale=${scale}:flags=lanczos" -c:v gif ${output}`
}
let progress = spawn(command, { shell: true })
progress.stderr.on('data', (data) => {
console.log(`FFmpeg Convert Log: ${data}`)
})
progress.on('exit', (code, signal) => {
console.log(`Recording process exited with code ${code} and signal ${signal}`)
fs.unlinkSync(fileName)
if (code == 0) {
shell.openPath(FILE_PATH)
progress = null
ffmpegProcess = null
}
})
区域窗口
下面来完结区域的录制,首先咱们要完结一个能框住某个区域的窗口。这样咱们在录制的时分,视频的录制规模便是区域的规模。
我在托盘菜单这儿加了一个选取区域的菜单,点击这个菜单的时分会翻开一个新窗口,这个新窗口是能够移动而且调整巨细的。后续的屏幕录制就会以这个窗口的巨细方位为基准录制。
完结逻辑并不杂乱,详细如下:
- 创建一个新窗口,
frame
为false
表明无边框,transparent
为true
表明透明窗口 -
loadURL
加载一个空的html
页面 -
dragWindow
方法完结全体窗口可拖动,详细在第一期完结过,这儿就不再赘述 -
executeJavaScript
给新窗口注入一些款式
let areaWindow
export const closeArea = () => {
if (areaWindow) {
areaWindow.close()
areaWindow = null
}
}
export const openRecordArea = () => {
if (areaWindow) {
closeArea()
return
}
const newWindow = new BrowserWindow({
width: 600,
height: 600,
frame: false,
transparent: true,
webPreferences: {
preload: PRELOAD_URL,
sandbox: false
}
})
areaWindow = newWindow
newWindow.loadURL(
`data:text/html;charset=utf-8,${encodeURIComponent('<html><body></body></html>')}`
)
newWindow.on('ready-to-show', () => {
newWindow.show()
})
newWindow.on('close', () => {
areaWindow = null
})
newWindow.webContents.on('did-finish-load', () => {
dragWindow(newWindow)
newWindow.webContents.executeJavaScript(`
const customStyles = `
html, body {
padding: 0;
margin: 0;
background: transparent;
border-radius:4px;
}
body {
border: 2px dashed #ccc;
}
#root {
display: none
}
`;
const styleTag = document.createElement('style');
styleTag.textContent = customStyles;
document.head.appendChild(styleTag);
`)
})
}
指定区域录制
开端录制之前要先介绍三个概念,物理像素、逻辑像素跟DPI。
物理像素是显示屏上的实际光点或发光元素,是硬件层面上的概念,它们直接映射到显示设备的硬件组件。
逻辑像素是在软件层面上的概念,是应用程序和操作系统中用于描述图像和界面的基本单位。逻辑像素通常不直接映射到硬件上的物理像素,而是由操作系统和图形引擎处理,以习惯不同的显示设备和分辨率。
DPI
代表“每英寸点数”(Dots Per Inch)
,它是一种用来度量打印设备、扫描仪、显示器或数字图像设备分辨率的单位。DPI表明在一英寸的空间内有多少个点或像素。
对于咱们的功用需求注意的便是,ffmpeg
操作的像素都是物理像素,而咱们的录制区域获取到的值都是逻辑像素。比如说咱们经过以下的代码获取录制区域的方位巨细信息:
const size = areaWindow.getSize()
const position = areaWindow.getPosition()
const [width, height] = size
const [left, top] = position
这儿的高度、宽度、间隔等等都是逻辑像素。
咱们是运用ffmpeg
的裁剪来录制指定区域,比如说下面的命令
ffmpeg -f avfoundation -r 30 -i "1" -vf "crop=800:600:100:100" output.mp4
-vf "crop=800:600:100:100"
的意思是裁剪视频,使其宽度为800
,高度为600
,而且从左上角坐标 (100, 100)
开端裁剪。
所以咱们在核算窗口巨细,即视频的分辨率巨细时,是需求将逻辑像素转化成物理像素的。物理像素=逻辑像素DPI
。所以咱们获取录制区域信息的时分,需求这样核算
let cropRect = {}
const size = areaWindow.getSize()
const position = areaWindow.getPosition()
const [width, height] = size
const [left, top] = position
const mainScreen = screen.getPrimaryDisplay()
const { scaleFactor } = mainScreen
cropRect = {
width: width * scaleFactor,
height: height * scaleFactor,
left: left * scaleFactor,
top: top * scaleFactor
}
接着就能够运用crop进行区域录制了
const { frameRate } = config
fileName = `${FILE_PATH}/${moment().format('YYYYMMDDHHmmss')}.mp4`
const cropString = !isEmpty(cropRect)
? `-vf "crop=${cropRect.width}:${cropRect.height}:${cropRect.left}:${cropRect.top}"`
: ''
/**统一先录制为mp4,避免受硬件影响 */
const ffmpegCommand = `${ffmpegPath} -f avfoundation -r ${frameRate} -i "1" ${cropString} -c:v libx264 -preset ultrafast ${fileName}`
这儿再看一个问题,假如我的选区是600*600
,DPI
为2
,那么录制出来的视频分辨率是多少呢?
没错,便是1200*1200
,那么再回到咱们上面的清晰度配置项,假如我的清晰度挑选了50%
,那么其实终究的产品文件分辨率应该是600*600
。所以在录制默许文件完结之后,还需求根据清晰度再去做一遍处理。
-
rate
便是挑选的清晰度 -
physicalWidth
、physicalHeight
分别对应屏幕的物理宽度、物理高度 - 原始的分辨率应该便是
physicalWidth * scaleFactor
,这个时分乘以挑选的清晰度便是设置的分辨率(physicalWidth * scaleFactor * rate)
- 算一下裁剪的区域占原始区域多少,再乘一下这个份额,便是终究指定区域的视频分辨率。(注意
cropRect.width
咱们已经乘过DPI
了)
let command
let rate = Number(definition)
const mainScreen = screen.getPrimaryDisplay()
const { scaleFactor } = mainScreen
const { width: physicalWidth, height: physicalHeight } = mainScreen.size
let scale = [physicalWidth * scaleFactor * rate, physicalHeight * scaleFactor * rate]
if (!isEmpty(cropRect)) {
const cropXRate = cropRect.width / (physicalWidth * scaleFactor)
const cropYRate = cropRect.height / (physicalHeight * scaleFactor)
scale = [scale[0] * cropXRate, scale[1] * cropYRate]
}
scale = `${Math.round(scale[0])}:${Math.round(scale[1])}`
command = `${ffmpegPath} -i ${fileName} -vf "scale=${scale}" -c:a copy ${output}`
最终
以上便是本文介绍的指定区域录屏功用,假如你有一些不同的想法,欢迎评论区交流。假如你觉得有所收获的话,点点关注点点赞吧~