h264-to-h265/index.js
2022-12-05 11:11:20 +08:00

130 lines
4.5 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

const fs = require('fs-extra');
const path = require('path');
const crypto = require('crypto');
const cmd = require('shelljs');
const cachePath = './cache.json';
const hardTypeSet = new Set(["none", "qsv", "cuda"]);
const supportVideoTypeSet = new Set(["mkv", "mp4"]);
const replaceTextArr = ['h264', 'H264', 'x264', 'X264'];
let cache = {};
let timer = setInterval(saveFile, 5000);
function saveFile () {
console.log("保存数据");
fs.writeFileSync(cachePath, JSON.stringify(cache));
}
(async () => {
if (fs.existsSync(cachePath)) {
cache = JSON.parse(fs.readFileSync(cachePath, 'utf-8'));
}
await deal("Z:\\userData\\视频", 3000, true, "cuda");
if (timer) {
clearInterval(timer);
}
saveFile();
})();
/**
*
* @param {*} basePath 处理路径
* @param {int} maxBitRate 最大码率,默认2500
* @param {boolean} changeName 是否改名true会将h264x264变更为h265,或者在文件结尾插入h265,其他名称相同额文件也会处理)
* @param {*} hardType 硬件加速类型,空/none不使用加速qsv:使用intel核显qsv加速cuda:使用nvdia cuda加速
* @returns
*/
async function deal (basePath, maxBitRate = 2500, changeName = false, hardType) {
let hwType = hardType == 'qsv' ? "-hwaccel qsv" : hardType == 'cuda' ? "-hwaccel cuda" : "";
let decodeType = hardType == 'qsv' ? "-c:v h264_qsv" : hardType == 'cuda' ? "-c:v h264_cuvid" : "";
let encodeType = hardType == 'qsv' ? "-c:v hevc_qsv" : hardType == 'cuda' ? "-c:v hevc_nvenc" : "";
if (hardType && !hardTypeSet.has(hardType)) {
throw new Error("不支持的加速方案:" + hardType + ",仅支持:空," + JSON.stringify(hardTypeSet));
}
let fileList = fs.readdirSync(basePath);
for (let i in fileList) {
let name = fileList[i];
let filePath = path.join(basePath, name);
if (!fs.existsSync(filePath)) {
continue;
}
if ((await fs.stat(filePath)).isDirectory()) {
//如果为文件夹递归处理
await deal(filePath, maxBitRate, changeName, hardType);
}
const md5Str = crypto.createHash('md5').update(filePath).digest('hex');
if (cache[md5Str]) {
continue;
}
cache[md5Str] = true;
if (!supportVideoTypeSet.has(name.substring(name.lastIndexOf(".") + 1))) {
continue;
}
console.log("---------开始处理:" + name);
let res = JSON.parse(cmd.exec(`ffprobe.exe "${filePath}" -show_streams -select_streams v -show_format -print_format json`, { encoding: 'utf-8' }).stdout);
if (!res.format || !res.streams || res.streams.length == 0) {
console.log("无法识别的格式:" + JSON.stringify(res));
continue;
}
let isH264 = res.streams.filter(item => item.codec_name === 'h264').length > 0;
if (!isH264) {
console.log("非h264,不处理");
continue;
}
let bitRate = res.format.bit_rate;
if (!bitRate) {
console.log("未获取到帧率,不处理", JSON.stringify(res));
continue;
}
let is10Bit = res.streams.filter(item => item.bits_per_raw_sample === '10').length > 0;
let originBitRate = Math.round(parseInt(bitRate) / 1000);
bitRate = originBitRate > maxBitRate ? maxBitRate : originBitRate * 0.7;
let newName = null;
replaceTextArr.forEach(item => {
if (newName == null && name.indexOf(item) > -1) {
newName = name.replace(item, 'h265');
}
})
if (newName == null) {
let index = name.lastIndexOf('.');
newName = name.substr(0, index) + ".h265" + name.substr(index);
}
let newFilePath = path.join(basePath, newName);
let cmdStr = `ffmpeg.exe ${hwType} ${is10Bit ? "" : decodeType} -i "${filePath}" -map 0 ${encodeType} -maxrate ${bitRate}K -c:a copy -c:s copy -y "${newFilePath}"`;
console.log(cmdStr);
let cmdRes = await cmd.exec(cmdStr);
let index = cmdRes.stderr.indexOf("video:");
if (index == -1) {
throw Error(cmdRes.stderr);
} else {
console.log("转换倍率:" + cmdRes.stderr.substring(index - 20));
}
if (!fs.existsSync(newFilePath) || fs.statSync(newFilePath).size <= 10000 || cmdRes.stderr.indexOf('Error') > -1) {
throw new Error("未知错误,文件转换失败");
}
if (changeName) {
//字幕nfo文件重命名
let namePart1 = name.substr(0, name.lastIndexOf('.'));
let newNamePart1 = newName.substr(0, newName.lastIndexOf('.'));
fileList.forEach(item => {
if (item.startsWith(namePart1) && item != name) {
let temp = item.replace(namePart1, newNamePart1);
if (item != temp) {
fs.renameSync(path.join(basePath, item), path.join(basePath, temp));
}
}
})
}
await fs.remove(filePath);
if (!changeName) {
await fs.move(newFilePath, filePath);
}
}
}