diff --git a/README.md b/README.md
index a2473e1..1fcf6c6 100644
--- a/README.md
+++ b/README.md
@@ -5,10 +5,7 @@ A tool to interactively (re-)encode videos using FFmpeg.
Uses Neutralino.js and Solid.js.
This app _tries_ to imitate KDE's Kirigami UI framework, and also makes use of
-Breeze icons
-
-- `./solid-src/src/assets/breeze[-dark]`: Icons used by TSX files
-- `./solid-src/public/breeze[-dark]`: Icons used by CSS files
+Breeze icons (Located in `./solid-src/public/breeze[-dark]`)
Vencoder is tested with FFmpeg 7.1.1, should be compatible with older versions
but is not guaranteed.
diff --git a/solid-src/src/App.tsx b/solid-src/src/App.tsx
index e7a02c5..9fab9e5 100644
--- a/solid-src/src/App.tsx
+++ b/solid-src/src/App.tsx
@@ -52,9 +52,19 @@ function App() {
RunningProcessInfo[]
>([]);
const [customFileExt, setCustomFileExt] = createSignal("");
+ const [globalopts, setGlobalopts] = createSignal("");
+ const [inputopts, setInputopts] = createSignal("");
+ const [outputopts, setOutputopts] = createSignal("");
const logs: { [id: number]: string[] } = {};
let supportedCodecs: CodecInfo[] = [];
- let ffmpegParams: FFmpegParams = { vcodec: "" };
+ let ffmpegParams: FFmpegParams = {
+ vcodec: "",
+ useropts: {
+ global: "",
+ input: "",
+ output: "",
+ },
+ };
let successfulCount = 0;
let unsuccessfulCount = 0;
let totalCount = 0;
@@ -195,6 +205,11 @@ function App() {
ffmpegParams = {
vcodec: codecObj?.shortName ?? "",
+ useropts: {
+ global: "",
+ input: "",
+ output: "",
+ },
};
let encoder = newValue;
@@ -242,6 +257,11 @@ function App() {
preset: ffmpegParams.preset,
twopass: ffmpegParams.twopass,
vbitrate: ffmpegParams.vbitrate,
+ useropts: {
+ global: globalopts(),
+ input: inputopts(),
+ output: outputopts(),
+ },
};
setOutputCommand(generateOutputCommand(ffmpegParams));
@@ -570,6 +590,49 @@ function App() {
/>
+
+
+
+
diff --git a/solid-src/src/components/encoders/librav1e.tsx b/solid-src/src/components/encoders/librav1e.tsx
index 4ddccd3..421c3a9 100644
--- a/solid-src/src/components/encoders/librav1e.tsx
+++ b/solid-src/src/components/encoders/librav1e.tsx
@@ -1,4 +1,8 @@
-import { type CodecInfo, type FFmpegParams } from "@/util/ffmpeg";
+import {
+ DEFAULT_BITRATE,
+ type CodecInfo,
+ type FFmpegParams,
+} from "@/util/ffmpeg";
import { os } from "@neutralinojs/lib";
import BreezeIcon from "@/components/BreezeIcon";
import { onMount } from "solid-js";
@@ -10,7 +14,10 @@ function Librav1eOptions(props: {
}) {
onMount(() => {
props.onParamChanged("crf", undefined);
- props.onParamChanged("vbitrate", undefined);
+ props.onParamChanged(
+ "vbitrate",
+ props.params.vbitrate ?? DEFAULT_BITRATE,
+ );
props.onParamChanged("speed", 5);
});
@@ -46,6 +53,19 @@ function Librav1eOptions(props: {
props.onParamChanged("speed", e.target.value)
}
/>
+
+
+
+ props.onParamChanged("vbitrate", e.target.value)
+ }
+ />
+ Kbps
+
);
diff --git a/solid-src/src/util/ffmpeg.ts b/solid-src/src/util/ffmpeg.ts
index 88f5f53..2cc6d00 100644
--- a/solid-src/src/util/ffmpeg.ts
+++ b/solid-src/src/util/ffmpeg.ts
@@ -72,6 +72,12 @@ export const videoFileExtensions: { [key: string]: string } = {
vp9: "mkv",
};
+export interface ExtraFFmpegArguments {
+ global: string;
+ input: string;
+ output: string;
+}
+
export interface FFmpegParams {
inputFile?: string;
outputFile?: string;
@@ -93,6 +99,10 @@ export interface FFmpegParams {
faststart?: boolean;
doNotUseAn?: boolean;
speed?: number;
+ /**
+ * Extra parameters defined by users
+ */
+ useropts: ExtraFFmpegArguments;
}
const NULL_LOCATION = window.NL_OS === "Windows" ? "NUL" : "/dev/null";
@@ -109,24 +119,34 @@ export function generateOutputCommand(params: FFmpegParams) {
? " -movflags +faststart"
: "";
+ let globalopts = "-hwaccel auto -y";
+ let inputopts =
+ params.useropts.input !== "" ? " " + params.useropts.input : "";
+ let outputopts =
+ params.useropts.output !== "" ? " " + params.useropts.output : "";
+
+ if (params.useropts.global !== "") {
+ globalopts += " " + params.useropts.global;
+ }
+
if (params.twopass) {
- const commonOpts = `-i "${params.inputFile ?? "{fileName}"}" -c:v ${params.encoder ?? params.vcodec} -b:v ${
+ const commonOpts = `${globalopts}${inputopts} -i "${params.inputFile ?? "{fileName}"}" -c:v ${params.encoder ?? params.vcodec} -b:v ${
params.vbitrate ?? DEFAULT_BITRATE
}k${faststart}${
params.preset === undefined ? "" : ` -preset ${params.preset}`
- } -progress -`;
+ } -progress -${outputopts}`;
- return `ffmpeg -hwaccel auto -y ${commonOpts} ${params.vcodec === "hevc" ? "-x265-params pass=1" : "-pass 1"} ${
+ return `ffmpeg ${commonOpts} ${params.vcodec === "hevc" ? "-x265-params pass=1" : "-pass 1"} ${
params.doNotUseAn ? "-vsync cfr" : "-an"
} -f null ${NULL_LOCATION} &&
-ffmpeg -y -hwaccel auto ${commonOpts} ${
+ffmpeg ${commonOpts} ${
params.vcodec === "hevc" ? "-x265-params pass=2" : "-pass 2"
} -c:a ${
params.acodec ?? "copy"
}${params.abitrate === undefined ? "" : ` -b:a ${params.abitrate}k`} "${params.outputFile ?? "{output}"}"`;
}
- return `ffmpeg -y -hwaccel auto -i "${params.inputFile ?? "{fileName}"}" -c:v ${params.encoder ?? params.vcodec}${
+ return `ffmpeg ${globalopts}${inputopts} -i "${params.inputFile ?? "{fileName}"}" -c:v ${params.encoder ?? params.vcodec}${
params.crf === undefined ? "" : ` -crf ${params.crf}`
}${
params.vbitrate === undefined ? "" : ` -b:v ${params.vbitrate}`
@@ -136,7 +156,7 @@ ffmpeg -y -hwaccel auto ${commonOpts} ${
params.abitrate === undefined ? "" : ` -b:a ${params.abitrate}k`
}${
params.speed === undefined ? "" : ` -speed ${params.speed}`
- } -progress - "${params.outputFile ?? "{output}"}"`;
+ } -progress -${outputopts} "${params.outputFile ?? "{output}"}"`;
}
export async function getLengthMicroseconds(target: string) {