mirror of
https://github.com/ragestudio/comty.git
synced 2025-06-09 10:34:17 +00:00
139 lines
3.0 KiB
JavaScript
139 lines
3.0 KiB
JavaScript
import fs from "node:fs"
|
|
import path from "node:path"
|
|
|
|
import { FFMPEGLib, Utils } from "../FFMPEGLib"
|
|
|
|
export default class MultiqualityHLSJob extends FFMPEGLib {
|
|
constructor(params = {}) {
|
|
super()
|
|
|
|
this.params = {
|
|
outputMasterName: "master.m3u8",
|
|
levels: [
|
|
{
|
|
original: true,
|
|
codec: "libx264",
|
|
bitrate: "10M",
|
|
preset: "ultrafast",
|
|
},
|
|
],
|
|
...params,
|
|
}
|
|
}
|
|
|
|
buildArgs = () => {
|
|
const cmdStr = [
|
|
`-v error -hide_banner -progress pipe:1`,
|
|
`-i ${this.params.input}`,
|
|
`-filter_complex`,
|
|
]
|
|
|
|
// set split args
|
|
let splitLevels = [`[0:v]split=${this.params.levels.length}`]
|
|
|
|
this.params.levels.forEach((level, i) => {
|
|
splitLevels[0] += `[v${i + 1}]`
|
|
})
|
|
|
|
for (const [index, level] of this.params.levels.entries()) {
|
|
if (level.original) {
|
|
splitLevels.push(`[v1]copy[v1out]`)
|
|
continue
|
|
}
|
|
|
|
let scaleFilter = `[v${index + 1}]scale=w=${level.width}:h=trunc(ow/a/2)*2[v${index + 1}out]`
|
|
|
|
splitLevels.push(scaleFilter)
|
|
}
|
|
|
|
cmdStr.push(`"${splitLevels.join(";")}"`)
|
|
|
|
// set levels map
|
|
for (const [index, level] of this.params.levels.entries()) {
|
|
let mapArgs = [
|
|
`-map "[v${index + 1}out]"`,
|
|
`-x264-params "nal-hrd=cbr:force-cfr=1"`,
|
|
`-c:v:${index} ${level.codec}`,
|
|
`-b:v:${index} ${level.bitrate}`,
|
|
`-maxrate:v:${index} ${level.bitrate}`,
|
|
`-minrate:v:${index} ${level.bitrate}`,
|
|
`-bufsize:v:${index} ${level.bitrate}`,
|
|
`-preset ${level.preset}`,
|
|
`-g 48`,
|
|
`-sc_threshold 0`,
|
|
`-keyint_min 48`,
|
|
]
|
|
|
|
cmdStr.push(...mapArgs)
|
|
}
|
|
|
|
// set output
|
|
cmdStr.push(`-f hls`)
|
|
cmdStr.push(`-hls_time 2`)
|
|
cmdStr.push(`-hls_playlist_type vod`)
|
|
cmdStr.push(`-hls_flags independent_segments`)
|
|
cmdStr.push(`-hls_segment_type mpegts`)
|
|
cmdStr.push(`-hls_segment_filename stream_%v/data%02d.ts`)
|
|
cmdStr.push(`-master_pl_name ${this.params.outputMasterName}`)
|
|
|
|
cmdStr.push(`-var_stream_map`)
|
|
|
|
let streamMapVar = []
|
|
|
|
for (const [index, level] of this.params.levels.entries()) {
|
|
streamMapVar.push(`v:${index}`)
|
|
}
|
|
|
|
cmdStr.push(`"${streamMapVar.join(" ")}"`)
|
|
cmdStr.push(`"stream_%v/stream.m3u8"`)
|
|
|
|
return cmdStr.join(" ")
|
|
}
|
|
|
|
run = async () => {
|
|
const cmdStr = this.buildArgs()
|
|
|
|
const outputPath =
|
|
this.params.outputDir ??
|
|
path.join(path.dirname(this.params.input), "hls")
|
|
const outputFile = path.join(outputPath, this.params.outputMasterName)
|
|
|
|
this.emit("start", {
|
|
input: this.params.input,
|
|
output: outputPath,
|
|
params: this.params,
|
|
})
|
|
|
|
if (!fs.existsSync(outputPath)) {
|
|
fs.mkdirSync(outputPath, { recursive: true })
|
|
}
|
|
|
|
const inputProbe = await Utils.probe(this.params.input)
|
|
|
|
try {
|
|
const result = await this.ffmpeg({
|
|
args: cmdStr,
|
|
cwd: outputPath,
|
|
onProcess: (process) => {
|
|
this.handleProgress(
|
|
process.stdout,
|
|
parseFloat(inputProbe.format.duration),
|
|
(progress) => {
|
|
this.emit("progress", progress)
|
|
},
|
|
)
|
|
},
|
|
})
|
|
|
|
this.emit("end", {
|
|
outputPath: outputPath,
|
|
outputFile: outputFile,
|
|
})
|
|
|
|
return result
|
|
} catch (err) {
|
|
return this.emit("error", err)
|
|
}
|
|
}
|
|
}
|