Refactor MultiqualityHLSJob to inherit from FFMPEGLib

This commit is contained in:
SrGooglo 2025-04-24 09:38:43 +00:00
parent 5842911fd1
commit 8c6ffadcc8
2 changed files with 110 additions and 124 deletions

View File

@ -1,45 +1,41 @@
import fs from "node:fs" import fs from "node:fs"
import path from "node:path" import path from "node:path"
import { exec } from "node:child_process"
import { EventEmitter } from "node:events"
export default class MultiqualityHLSJob { import { FFMPEGLib, Utils } from "../FFMPEGLib"
constructor({
input,
outputDir,
outputMasterName = "master.m3u8",
levels,
}) {
this.input = input
this.outputDir = outputDir
this.levels = levels
this.outputMasterName = outputMasterName
this.bin = require("ffmpeg-static") export default class MultiqualityHLSJob extends FFMPEGLib {
constructor(params = {}) {
super()
return this this.params = {
outputMasterName: "master.m3u8",
levels: [
{
original: true,
codec: "libx264",
bitrate: "10M",
preset: "ultrafast",
},
],
...params,
}
} }
events = new EventEmitter() buildArgs = () => {
buildCommand = () => {
const cmdStr = [ const cmdStr = [
this.bin, `-v error -hide_banner -progress pipe:1`,
`-v quiet -stats`, `-i ${this.params.input}`,
`-i ${this.input}`,
`-filter_complex`, `-filter_complex`,
] ]
// set split args // set split args
let splitLevels = [ let splitLevels = [`[0:v]split=${this.params.levels.length}`]
`[0:v]split=${this.levels.length}`
]
this.levels.forEach((level, i) => { this.params.levels.forEach((level, i) => {
splitLevels[0] += (`[v${i + 1}]`) splitLevels[0] += `[v${i + 1}]`
}) })
for (const [index, level] of this.levels.entries()) { for (const [index, level] of this.params.levels.entries()) {
if (level.original) { if (level.original) {
splitLevels.push(`[v1]copy[v1out]`) splitLevels.push(`[v1]copy[v1out]`)
continue continue
@ -53,7 +49,7 @@ export default class MultiqualityHLSJob {
cmdStr.push(`"${splitLevels.join(";")}"`) cmdStr.push(`"${splitLevels.join(";")}"`)
// set levels map // set levels map
for (const [index, level] of this.levels.entries()) { for (const [index, level] of this.params.levels.entries()) {
let mapArgs = [ let mapArgs = [
`-map "[v${index + 1}out]"`, `-map "[v${index + 1}out]"`,
`-x264-params "nal-hrd=cbr:force-cfr=1"`, `-x264-params "nal-hrd=cbr:force-cfr=1"`,
@ -78,13 +74,13 @@ export default class MultiqualityHLSJob {
cmdStr.push(`-hls_flags independent_segments`) cmdStr.push(`-hls_flags independent_segments`)
cmdStr.push(`-hls_segment_type mpegts`) cmdStr.push(`-hls_segment_type mpegts`)
cmdStr.push(`-hls_segment_filename stream_%v/data%02d.ts`) cmdStr.push(`-hls_segment_filename stream_%v/data%02d.ts`)
cmdStr.push(`-master_pl_name ${this.outputMasterName}`) cmdStr.push(`-master_pl_name ${this.params.outputMasterName}`)
cmdStr.push(`-var_stream_map`) cmdStr.push(`-var_stream_map`)
let streamMapVar = [] let streamMapVar = []
for (const [index, level] of this.levels.entries()) { for (const [index, level] of this.params.levels.entries()) {
streamMapVar.push(`v:${index}`) streamMapVar.push(`v:${index}`)
} }
@ -94,54 +90,49 @@ export default class MultiqualityHLSJob {
return cmdStr.join(" ") return cmdStr.join(" ")
} }
run = () => { run = async () => {
const cmdStr = this.buildCommand() const cmdStr = this.buildArgs()
console.log(cmdStr) const outputPath =
this.params.outputDir ??
path.join(path.dirname(this.params.input), "hls")
const outputFile = path.join(outputPath, this.params.outputMasterName)
const cwd = `${path.dirname(this.input)}/hls` this.emit("start", {
input: this.params.input,
if (!fs.existsSync(cwd)) { output: outputPath,
fs.mkdirSync(cwd, { recursive: true }) params: this.params,
}
console.log(`[HLS] Started multiquality transcode`, {
input: this.input,
cwd: cwd,
}) })
const process = exec( if (!fs.existsSync(outputPath)) {
cmdStr, fs.mkdirSync(outputPath, { recursive: true })
{ }
cwd: cwd,
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)
}, },
(error, stdout, stderr) => {
if (error) {
console.log(`[HLS] Failed to transcode >`, error)
return this.events.emit("error", error)
}
if (stderr) {
//return this.events.emit("error", stderr)
}
console.log(`[HLS] Finished transcode >`, cwd)
return this.events.emit("end", {
filepath: path.join(cwd, this.outputMasterName),
isDirectory: true,
})
}
) )
},
process.stdout.on("data", (data) => {
console.log(data.toString())
}) })
}
on = (key, cb) => { this.emit("end", {
this.events.on(key, cb) outputPath: outputPath,
return this outputFile: outputFile,
})
return result
} catch (err) {
return this.emit("error", err)
}
} }
} }

View File

@ -27,12 +27,7 @@ export default async ({ filePath, workPath, onProgress }) => {
], ],
}) })
job.on("start", () => {
console.log("A-DASH started")
})
job.on("end", (data) => { job.on("end", (data) => {
console.log("A-DASH completed", data)
resolve(data) resolve(data)
}) })