H.264 & H.265 QuickSync
Build / build (push) Has been cancelled

This commit is contained in:
2025-10-02 15:59:39 +07:00
parent a949e5d46b
commit 64cfdce699
11 changed files with 625 additions and 518 deletions
+22 -14
View File
@@ -31,7 +31,7 @@ import BreezeIcon from "./components/BreezeIcon";
import AV1Options from "./components/AV1Options";
import DNxHDOptions from "./components/DNxHDOptions";
const commonCodecs = new Set(["h264", "hevc", "vp8", "vp9", "av1", "dnxhd"]);
const commonCodecs = new Set(["h264", "hevc", "vp9", "av1", "dnxhd"]);
interface RunningProcessInfo {
process: Neutralino.SpawnedProcess;
@@ -212,27 +212,25 @@ function App() {
(v) => v.shortName === newValue,
);
if (newValue !== "h264" && newValue !== "hevc") {
ffmpegParams.twopass = false;
let encoder = newValue;
if (codecObj?.encoders.length !== 0) {
encoder = codecObj?.encoders[0] ?? "";
}
setSelectedCodec(codecObj);
setSelectedEncoder(encoder);
}
createEffect(() => {
ffmpegParams = {
vcodec: codecObj?.shortName ?? "",
vcodec: selectedCodec()?.shortName ?? "",
encoder: selectedEncoder(),
useropts: {
global: "",
input: "",
output: "",
},
};
let encoder = newValue;
if (codecObj?.encoders.length !== 0) {
encoder = codecObj?.encoders[0] ?? "";
}
ffmpegParams.encoder = encoder;
setSelectedCodec(codecObj);
setSelectedEncoder(encoder);
}
});
function getAudioEncoders() {
const codec = audioCodec();
@@ -581,7 +579,16 @@ function App() {
</For>
</select>
</form>
<Switch fallback={<div></div>}>
<div class="row flex-col align-items-center">
<h3 class="k-form-section-title">
Encoder Options
</h3>
</div>
<Switch
fallback={
<div class="text-center mt-4">No options.</div>
}
>
<Match
when={
selectedCodec()?.shortName === "h264" ||
@@ -590,6 +597,7 @@ function App() {
>
<H264Options
codec={selectedCodec()}
encoder={selectedEncoder()}
params={ffmpegParams}
onParamChanged={onParametersChanged}
/>
+28 -35
View File
@@ -15,43 +15,36 @@ function DNxHDOptions(props: {
onParamChanged: FFmpegParamChangedFunc;
}) {
return (
<section id="commonLossyOptions">
<div class="row flex-col align-items-center">
<h3 class="k-form-section-title">Encoder Options</h3>
</div>
<div class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() =>
os.open("https://askubuntu.com/a/907515")
}
title="DNxHD is a picky encoder."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="profile">Profile</label>
<select
class="k-dropdown"
name="profile"
id="profile"
value={props.params.outputopts?.profile ?? "dnxhd"}
oninput={(e) => {
props.onParamChanged("outputopts", {
profile: e.target.value,
});
}}
<section id="commonLossyOptions" class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() => os.open("https://askubuntu.com/a/907515")}
title="DNxHD is a picky encoder."
>
<option value="dnxhd">DNxHD</option>
<option value="dnxhr_444">DNxHR 444</option>
<option value="dnxhr_hqx">DNxHR HQX</option>
<option value="dnxhr_hq">DNxHR HQ</option>
<option value="dnxhr_sq">DNxHR SQ</option>
<option value="dnxhr_lb">DNxHR LB</option>
</select>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="profile">Profile</label>
<select
class="k-dropdown"
name="profile"
id="profile"
value={props.params.outputopts?.profile ?? "dnxhd"}
oninput={(e) => {
props.onParamChanged("outputopts", {
profile: e.target.value,
});
}}
>
<option value="dnxhd">DNxHD</option>
<option value="dnxhr_444">DNxHR 444</option>
<option value="dnxhr_hqx">DNxHR HQX</option>
<option value="dnxhr_hq">DNxHR HQ</option>
<option value="dnxhr_sq">DNxHR SQ</option>
<option value="dnxhr_lb">DNxHR LB</option>
</select>
</section>
);
}
+22 -152
View File
@@ -1,170 +1,40 @@
import { createSignal, Show } from "solid-js";
import { Match, Switch } from "solid-js";
import {
DEFAULT_BITRATE,
type CodecInfo,
type FFmpegParamChangedFunc,
type FFmpegParams,
} from "../util/ffmpeg";
import { os } from "@neutralinojs/lib";
import BreezeIcon from "./BreezeIcon";
const information = {
h264: {
defaultCrf: 23,
},
hevc: {
defaultCrf: 28,
},
};
import LibH26xOptions from "./encoders/libx264";
import H264QsvOptions from "./encoders/h264qsv";
/**
* Options for H.264/H.265 codecs
*/
function H264Options(props: {
codec: CodecInfo | undefined;
encoder: string;
params: FFmpegParams;
onParamChanged: FFmpegParamChangedFunc;
}) {
const [twopass, setTwopass] = createSignal(false);
const defaultCrf =
props.codec?.shortName === "h264"
? information.h264.defaultCrf
: information.hevc.defaultCrf;
return (
<section id="commonLossyOptions">
<div class="row flex-col align-items-center">
<h3 class="k-form-section-title">Encoder Options</h3>
</div>
<div class="k-form">
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.twopass?.toString()}
onInput={(e) => {
props.params.twopass = e.target.checked;
props.onParamChanged("twopass", e.target.checked);
setTwopass(e.target.checked);
}}
id="twopassCheck"
/>
<label for="twopassCheck">
Use target bitrate instead of CRF
</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#twopass",
)
}
title="This will use the two-pass rate control mode instead of relying on a Constant Rate Factor (CRF) value."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="encodingPreset">Preset</label>
<select
class="k-dropdown"
name="encodingPreset"
id="encodingPreset"
value={props.params.preset ?? "medium"}
oninput={(e) => {
props.params.preset = e.target.value;
props.onParamChanged("preset", e.target.value);
}}
>
<option value="ultrafast">ultrafast</option>
<option value="superfast">superfast</option>
<option value="veryfast">veryfast</option>
<option value="faster">faster</option>
<option value="fast">fast</option>
<option value="medium">medium (Default)</option>
<option value="slow">slow</option>
<option value="slower">slower</option>
<option value="veryslow">veryslow</option>
<option
value="placebo"
title="Don't use this option, it rarely helps."
>
placebo
</option>
</select>
<Show
when={twopass()}
fallback={
<>
<label for="crf">CRF</label>
<input
type="number"
name="crf"
id="crf"
min="0"
max="51"
value={props.params.crf ?? defaultCrf}
oninput={(e) => {
props.params.crf = parseInt(e.target.value);
props.onParamChanged(
"crf",
parseInt(e.target.value),
);
}}
/>
</>
}
>
<label for="bitrate">Bitrate</label>
<div>
<input
type="number"
name="bitrate"
id="bitrate"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
oninput={(e) => {
props.params.vbitrate = parseInt(
e.target.value,
);
props.onParamChanged(
"vbitrate",
parseInt(e.target.value),
);
}}
/>
<span> Kbps</span>
</div>
</Show>
<Show when={props.codec?.shortName === "h264"}>
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.faststart?.toString()}
onInput={(e) => {
props.params.faststart = e.target.checked;
props.onParamChanged(
"faststart",
e.target.checked,
);
}}
id="fastStartCheck"
/>
<label for="fastStartCheck">Enable Fast Start</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#faststartforwebvideo",
)
}
title="This will move some information to the beginning of your file and allow the video to begin playing before it is completely downloaded by the viewer, recommended for web videos. Click for more information."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
</Show>
</div>
</section>
<Switch fallback={<div class="text-center mt-4">No options.</div>}>
<Match
when={
props.encoder === "libx264" ||
props.encoder === "libx264rgb" ||
props.encoder === "libx265"
}
>
<LibH26xOptions {...props} />
</Match>
<Match
when={
props.encoder === "h264_qsv" || props.encoder === "hevc_qsv"
}
>
<H264QsvOptions {...props} />
</Match>
</Switch>
);
}
+111 -121
View File
@@ -16,138 +16,128 @@ function VP9Options(props: {
const defaultCrf = 30;
return (
<section id="commonLossyOptions">
<div class="row flex-col align-items-center">
<h3 class="k-form-section-title">Encoder Options</h3>
</div>
<div class="k-form">
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.twopass?.toString()}
onInput={(e) => {
props.params.twopass = e.target.checked;
props.onParamChanged("twopass", e.target.checked);
setTwopass(e.target.checked);
}}
id="twopassCheck"
/>
<label for="twopassCheck">
Use target bitrate instead of CRF
</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#twopass",
)
}
title="This will use the two-pass rate control mode instead of relying on a Constant Rate Factor (CRF) value."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="encodingPreset">Preset</label>
<select
class="k-dropdown"
name="encodingPreset"
id="encodingPreset"
value={props.params.preset ?? "medium"}
oninput={(e) => {
props.params.preset = e.target.value;
props.onParamChanged("preset", e.target.value);
<section id="commonLossyOptions" class="k-form">
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.twopass?.toString()}
onInput={(e) => {
props.params.twopass = e.target.checked;
props.onParamChanged("twopass", e.target.checked);
setTwopass(e.target.checked);
}}
>
<option value="ultrafast">ultrafast</option>
<option value="superfast">superfast</option>
<option value="veryfast">veryfast</option>
<option value="faster">faster</option>
<option value="fast">fast</option>
<option value="medium">medium (Default)</option>
<option value="slow">slow</option>
<option value="slower">slower</option>
<option value="veryslow">veryslow</option>
<option
value="placebo"
title="Don't use this option, it rarely helps."
>
placebo
</option>
</select>
<Show
when={twopass()}
fallback={
<>
<label for="crf">CRF</label>
<input
type="number"
name="crf"
id="crf"
min="0"
max="51"
value={props.params.crf ?? defaultCrf}
oninput={(e) => {
props.params.crf = parseInt(e.target.value);
props.onParamChanged(
"crf",
parseInt(e.target.value),
);
}}
/>
</>
id="twopassCheck"
/>
<label for="twopassCheck">
Use target bitrate instead of CRF
</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#twopass",
)
}
title="This will use the two-pass rate control mode instead of relying on a Constant Rate Factor (CRF) value."
>
<label for="bitrate">Bitrate</label>
<div>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="encodingPreset">Preset</label>
<select
class="k-dropdown"
name="encodingPreset"
id="encodingPreset"
value={props.params.preset ?? "medium"}
oninput={(e) => {
props.params.preset = e.target.value;
props.onParamChanged("preset", e.target.value);
}}
>
<option value="ultrafast">ultrafast</option>
<option value="superfast">superfast</option>
<option value="veryfast">veryfast</option>
<option value="faster">faster</option>
<option value="fast">fast</option>
<option value="medium">medium (Default)</option>
<option value="slow">slow</option>
<option value="slower">slower</option>
<option value="veryslow">veryslow</option>
<option
value="placebo"
title="Don't use this option, it rarely helps."
>
placebo
</option>
</select>
<Show
when={twopass()}
fallback={
<>
<label for="crf">CRF</label>
<input
type="number"
name="bitrate"
id="bitrate"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
name="crf"
id="crf"
min="0"
max="51"
value={props.params.crf ?? defaultCrf}
oninput={(e) => {
props.params.vbitrate = parseInt(
e.target.value,
);
props.params.crf = parseInt(e.target.value);
props.onParamChanged(
"vbitrate",
"crf",
parseInt(e.target.value),
);
}}
/>
<span> Kbps</span>
</div>
</Show>
<Show when={props.codec?.shortName === "h264"}>
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.faststart?.toString()}
onInput={(e) => {
props.params.faststart = e.target.checked;
props.onParamChanged(
"faststart",
e.target.checked,
);
}}
id="fastStartCheck"
/>
<label for="fastStartCheck">Enable Fast Start</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#faststartforwebvideo",
)
}
title="This will move some information to the beginning of your file and allow the video to begin playing before it is completely downloaded by the viewer, recommended for web videos. Click for more information."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
</Show>
</div>
</>
}
>
<label for="bitrate">Bitrate</label>
<div>
<input
type="number"
name="bitrate"
id="bitrate"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
oninput={(e) => {
props.params.vbitrate = parseInt(e.target.value);
props.onParamChanged(
"vbitrate",
parseInt(e.target.value),
);
}}
/>
<span> Kbps</span>
</div>
</Show>
<Show when={props.codec?.shortName === "h264"}>
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.faststart?.toString()}
onInput={(e) => {
props.params.faststart = e.target.checked;
props.onParamChanged("faststart", e.target.checked);
}}
id="fastStartCheck"
/>
<label for="fastStartCheck">Enable Fast Start</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#faststartforwebvideo",
)
}
title="This will move some information to the beginning of your file and allow the video to begin playing before it is completely downloaded by the viewer, recommended for web videos. Click for more information."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
</Show>
</section>
);
}
@@ -0,0 +1,118 @@
import {
DEFAULT_BITRATE,
type CodecInfo,
type FFmpegParamChangedFunc,
type FFmpegParams,
} from "@/util/ffmpeg";
import { createEffect, createSignal, onMount, Show } from "solid-js";
function H264QsvOptions({
onParamChanged,
}: {
codec: CodecInfo | undefined;
params: FFmpegParams;
onParamChanged: FFmpegParamChangedFunc;
}) {
const [rateControl, setRateControl] = createSignal("icq");
const [globalQuality, setGlobalQuality] = createSignal(18);
const [bitrate, setBitrate] = createSignal(DEFAULT_BITRATE);
const [cqp, setCqp] = createSignal(18);
createEffect(() => {
const opts: Record<string, string> = {};
switch (rateControl()) {
case "icq":
onParamChanged("vbitrate", undefined);
const quality = globalQuality();
opts["global_quality"] = quality.toString();
break;
case "cbr":
onParamChanged("vbitrate", bitrate());
opts["maxrate"] = `${bitrate() ?? DEFAULT_BITRATE}k`;
break;
case "vbr":
onParamChanged("vbitrate", bitrate());
break;
case "cqp":
onParamChanged("vbitrate", undefined);
opts["q"] = cqp().toString();
break;
}
onParamChanged("outputopts", opts);
});
onMount(() => {
onParamChanged("hwaccel", "qsv");
});
return (
<section id="encoderOptions" class="k-form">
<label for="rateControl">Rate Control Mode</label>
<select
name="rateControl"
id="rateControl"
class="k-dropdown"
value={rateControl()}
oninput={(e) => setRateControl(e.target.value)}
>
<option value="cbr">CBR: Constant Bitrate</option>
<option value="cqp">
CQP: Constant Quantization Parameter
</option>
<option value="vbr">VBR: Variable Bitrate</option>
<option value="icq">ICQ: Intelligent Constant Quality</option>
</select>
<Show when={rateControl() === "icq"}>
<label for="globalQuality">Global Quality</label>
<input
type="number"
name="globalQuality"
id="globalQuality"
min="1"
max="51"
value={globalQuality()}
oninput={(e) => setGlobalQuality(parseInt(e.target.value))}
/>
</Show>
<Show when={rateControl() === "cqp"}>
<label for="cqp">CQP</label>
<input
type="number"
name="cqp"
id="cqp"
min="1"
max="51"
value={cqp()}
oninput={(e) => setCqp(parseInt(e.target.value))}
/>
</Show>
<Show when={rateControl() === "cbr" || rateControl() === "vbr"}>
<label for="bitrate">Bitrate</label>
<div>
<input
type="number"
name="bitrate"
id="bitrate"
value={bitrate()}
oninput={(e) => setBitrate(parseInt(e.target.value))}
/>
<span> Kbps</span>
</div>
</Show>
<Show when={rateControl() === "vbr" || rateControl() === "icq"}>
<div></div>
<div
class="checkbox-container"
title="May not be available on some platforms."
>
<input type="checkbox" name="lookAhead" id="lookAhead" />
<label for="lookAhead">Look-ahead mode</label>
</div>
</Show>
</section>
);
}
export default H264QsvOptions;
+55 -63
View File
@@ -46,79 +46,71 @@ function LibaomOptions(props: {
});
return (
<section id="encoderOptions">
<div class="row flex-col align-items-center">
<h3 class="k-form-section-title">Encoder Options</h3>
</div>
<div class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/AV1#libaom",
)
}
title="Click to view the documentation for this encoder."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="rateControlMode">Rate-control modes</label>
<select
class="k-dropdown"
onchange={(e) => setRateControlMode(e.target.value)}
name="rateControlMode"
id="rateControlMode"
>
<option value="Constant">Constant Quality</option>
<option value="Constrained">Constrained Quality</option>
<option value="2PassABR">2-Pass Average Bitrate</option>
<option value="ABR">1-Pass Average Bitrate</option>
</select>
<Show
when={
rateControlMode() === "Constant" ||
rateControlMode() === "Constrained"
<section id="encoderOptions" class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/AV1#libaom",
)
}
title="Click to view the documentation for this encoder."
>
<label for="crf">CRF</label>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="rateControlMode">Rate-control modes</label>
<select
class="k-dropdown"
onchange={(e) => setRateControlMode(e.target.value)}
name="rateControlMode"
id="rateControlMode"
>
<option value="Constant">Constant Quality</option>
<option value="Constrained">Constrained Quality</option>
<option value="2PassABR">2-Pass Average Bitrate</option>
<option value="ABR">1-Pass Average Bitrate</option>
</select>
<Show
when={
rateControlMode() === "Constant" ||
rateControlMode() === "Constrained"
}
>
<label for="crf">CRF</label>
<input
type="number"
name="crf"
id="crf"
min="1"
max="63"
value={props.params.crf ?? DEFAULT_CRF}
oninput={(e) => {
props.onParamChanged("crf", parseInt(e.target.value));
}}
/>
</Show>
<Show when={rateControlMode() !== "Constant"}>
<label for="bitrate">Bitrate</label>
<div class="row gap2 align-items-center">
<input
type="number"
name="crf"
id="crf"
min="1"
max="63"
value={props.params.crf ?? DEFAULT_CRF}
name="bitrate"
id="bitrate"
aria-label="Kbps"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
oninput={(e) => {
props.onParamChanged(
"crf",
"vbitrate",
parseInt(e.target.value),
);
}}
/>
</Show>
<Show when={rateControlMode() !== "Constant"}>
<label for="bitrate">Bitrate</label>
<div class="row gap2 align-items-center">
<input
type="number"
name="bitrate"
id="bitrate"
aria-label="Kbps"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
oninput={(e) => {
props.onParamChanged(
"vbitrate",
parseInt(e.target.value),
);
}}
/>
<span>Kbps</span>
</div>
</Show>
</div>
<span>Kbps</span>
</div>
</Show>
</section>
);
}
+36 -42
View File
@@ -14,7 +14,6 @@ function Librav1eOptions(props: {
onParamChanged: FFmpegParamChangedFunc;
}) {
onMount(() => {
props.onParamChanged("crf", undefined);
props.onParamChanged(
"vbitrate",
props.params.vbitrate ?? DEFAULT_BITRATE,
@@ -23,53 +22,48 @@ function Librav1eOptions(props: {
});
return (
<section id="encoderOptions">
<div class="row flex-col align-items-center">
<h3 class="k-form-section-title">Encoder Options</h3>
<section id="encoderOptions" class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() =>
os.open(
"https://www.ffmpeg.org/ffmpeg-all.html#librav1e",
)
}
title="Click to view the documentation for this encoder."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<div class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() =>
os.open(
"https://www.ffmpeg.org/ffmpeg-all.html#librav1e",
)
}
title="Click to view the documentation for this encoder."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label>Speed</label>
<label>Speed</label>
<input
type="number"
name="speed"
id="speed"
min="0"
max="10"
value={props.params.speed ?? 5}
oninput={(e) =>
props.onParamChanged("speed", parseInt(e.target.value))
}
/>
<label for="bitrate">Bitrate</label>
<div class="row gap2 align-items-center">
<input
type="number"
name="speed"
id="speed"
min="0"
max="10"
value={props.params.speed ?? 5}
name="bitrate"
id="bitrate"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
oninput={(e) =>
props.onParamChanged("speed", parseInt(e.target.value))
props.onParamChanged(
"vbitrate",
parseInt(e.target.value),
)
}
/>
<label for="bitrate">Bitrate</label>
<div class="row gap2 align-items-center">
<input
type="number"
name="bitrate"
id="bitrate"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
oninput={(e) =>
props.onParamChanged(
"vbitrate",
parseInt(e.target.value),
)
}
/>
<span>Kbps</span>
</div>
<span>Kbps</span>
</div>
</section>
);
+65 -80
View File
@@ -33,90 +33,75 @@ function LibSvtAv1Options({
});
});
onMount(() => {
onParamChanged("vbitrate", undefined);
if (isNaN(parseInt(params.preset ?? ""))) {
onParamChanged("preset", "5");
}
});
return (
<section id="encoderOptions">
<div class="row flex-col align-items-center">
<h3 class="k-form-section-title">Encoder Options</h3>
</div>
<div class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() =>
os.open(
"https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Ffmpeg.md",
)
}
title="Click to view the documentation for this encoder."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="preset">Preset</label>
<input
type="number"
name="preset"
id="preset"
title="A range from -2 to 13. Higher means faster but a loss in quality. Preset 13 is not intended for human use."
value={params.preset ?? 5}
oninput={(e) => onParamChanged("preset", e.target.value)}
min="-2"
max="13"
/>
<label for="crf">CRF</label>
<input
type="number"
name="crf"
id="crf"
title="A range from 1 to 63. A good starting point for a 1080p video is 30"
value={params.crf ?? 30}
oninput={(e) =>
onParamChanged("crf", parseInt(e.target.value))
<section id="encoderOptions" class="k-form">
<label>Help</label>
<div>
<button
class="icon-button"
onclick={() =>
os.open(
"https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Ffmpeg.md",
)
}
min="1"
max="63"
/>
<label for="gop">GOP</label>
<input
type="number"
name="gop"
id="gop"
title="How many frames will pass before the encoder will add a key frame. Specify -1 to leave the parameter unspecified."
value={gop()}
oninput={(e) => setGop(e.target.value)}
min="-1"
/>
<label for="filmGrain">Film Grain</label>
<input
type="number"
name="filmGrain"
id="filmGrain"
title="Film grains are hard to compress. The encoder can try to replace the film grain in the video with a synthetic grain."
value={filmGrain()}
oninput={(e) => setFilmGrain(e.target.value)}
min="0"
/>
<label for="tune">Tune</label>
<select
name="tune"
id="tune"
class="k-dropdown"
value={tune()}
oninput={(e) => setTune(e.target.value)}
title="Click to view the documentation for this encoder."
>
<option value="0">Subjective Quality</option>
<option value="1">Objective Quality (PSNR)</option>
</select>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="preset">Preset</label>
<input
type="number"
name="preset"
id="preset"
title="A range from -2 to 13. Higher means faster but a loss in quality. Preset 13 is not intended for human use."
value={params.preset ?? 5}
oninput={(e) => onParamChanged("preset", e.target.value)}
min="-2"
max="13"
/>
<label for="crf">CRF</label>
<input
type="number"
name="crf"
id="crf"
title="A range from 1 to 63. A good starting point for a 1080p video is 30"
value={params.crf ?? 30}
oninput={(e) => onParamChanged("crf", parseInt(e.target.value))}
min="1"
max="63"
/>
<label for="gop">GOP</label>
<input
type="number"
name="gop"
id="gop"
title="How many frames will pass before the encoder will add a key frame. Specify -1 to leave the parameter unspecified."
value={gop()}
oninput={(e) => setGop(e.target.value)}
min="-1"
/>
<label for="filmGrain">Film Grain</label>
<input
type="number"
name="filmGrain"
id="filmGrain"
title="Film grains are hard to compress. The encoder can try to replace the film grain in the video with a synthetic grain."
value={filmGrain()}
oninput={(e) => setFilmGrain(e.target.value)}
min="0"
/>
<label for="tune">Tune</label>
<select
name="tune"
id="tune"
class="k-dropdown"
value={tune()}
oninput={(e) => setTune(e.target.value)}
>
<option value="0">Subjective Quality</option>
<option value="1">Objective Quality (PSNR)</option>
</select>
</section>
);
}
@@ -0,0 +1,160 @@
import { createSignal, Show } from "solid-js";
import {
DEFAULT_BITRATE,
type CodecInfo,
type FFmpegParamChangedFunc,
type FFmpegParams,
} from "@/util/ffmpeg";
import { os } from "@neutralinojs/lib";
import BreezeIcon from "@/components/BreezeIcon";
const information = {
h264: {
defaultCrf: 23,
},
hevc: {
defaultCrf: 28,
},
};
/**
* Options for H.264/H.265 codecs
*/
function LibH26xOptions(props: {
codec: CodecInfo | undefined;
params: FFmpegParams;
onParamChanged: FFmpegParamChangedFunc;
}) {
const [twopass, setTwopass] = createSignal(false);
const defaultCrf =
props.codec?.shortName === "h264"
? information.h264.defaultCrf
: information.hevc.defaultCrf;
return (
<section id="commonLossyOptions" class="k-form">
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.twopass?.toString()}
onInput={(e) => {
props.params.twopass = e.target.checked;
props.onParamChanged("twopass", e.target.checked);
setTwopass(e.target.checked);
}}
id="twopassCheck"
/>
<label for="twopassCheck">
Use target bitrate instead of CRF
</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#twopass",
)
}
title="This will use the two-pass rate control mode instead of relying on a Constant Rate Factor (CRF) value."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
<label for="encodingPreset">Preset</label>
<select
class="k-dropdown"
name="encodingPreset"
id="encodingPreset"
value={props.params.preset ?? "medium"}
oninput={(e) => {
props.params.preset = e.target.value;
props.onParamChanged("preset", e.target.value);
}}
>
<option value="ultrafast">ultrafast</option>
<option value="superfast">superfast</option>
<option value="veryfast">veryfast</option>
<option value="faster">faster</option>
<option value="fast">fast</option>
<option value="medium">medium (Default)</option>
<option value="slow">slow</option>
<option value="slower">slower</option>
<option value="veryslow">veryslow</option>
<option
value="placebo"
title="Don't use this option, it rarely helps."
>
placebo
</option>
</select>
<Show
when={twopass()}
fallback={
<>
<label for="crf">CRF</label>
<input
type="number"
name="crf"
id="crf"
min="0"
max="51"
value={props.params.crf ?? defaultCrf}
oninput={(e) => {
props.params.crf = parseInt(e.target.value);
props.onParamChanged(
"crf",
parseInt(e.target.value),
);
}}
/>
</>
}
>
<label for="bitrate">Bitrate</label>
<div>
<input
type="number"
name="bitrate"
id="bitrate"
value={props.params.vbitrate ?? DEFAULT_BITRATE}
oninput={(e) =>
props.onParamChanged(
"vbitrate",
parseInt(e.target.value),
)
}
/>
<span> Kbps</span>
</div>
</Show>
<Show when={props.codec?.shortName === "h264"}>
<div></div>
<div class="checkbox-container">
<input
type="checkbox"
value={props.params.faststart?.toString()}
onInput={(e) => {
props.params.faststart = e.target.checked;
props.onParamChanged("faststart", e.target.checked);
}}
id="fastStartCheck"
/>
<label for="fastStartCheck">Enable Fast Start</label>
<button
class="icon-button"
onclick={() =>
os.open(
"https://trac.ffmpeg.org/wiki/Encode/H.264#faststartforwebvideo",
)
}
title="This will move some information to the beginning of your file and allow the video to begin playing before it is completely downloaded by the viewer, recommended for web videos. Click for more information."
>
<BreezeIcon icon="help-about" alt="Help" />
</button>
</div>
</Show>
</section>
);
}
export default LibH26xOptions;
+2 -2
View File
@@ -171,7 +171,7 @@ export function generateOutputCommand(params: FFmpegParams) {
? " -movflags +faststart"
: "";
let globalopts = "-hwaccel auto -y";
let globalopts = `-hwaccel ${params.hwaccel ?? "auto"} -y`;
let inputopts =
params.useropts.input !== "" ? " " + params.useropts.input : "";
let outputopts =
@@ -220,7 +220,7 @@ ffmpeg ${commonOpts} ${params.vcodec === "hevc" ? "-x265-params pass=2" : "-pass
}
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}`
}${params.vbitrate === undefined ? "" : ` -b:v ${params.vbitrate}k`
}${faststart}${params.preset === undefined ? "" : ` -preset ${params.preset}`
} -c:a ${params.acodec ?? "copy"}${params.abitrate === undefined ? "" : ` -b:a ${params.abitrate}k`
}${params.speed === undefined ? "" : ` -speed ${params.speed}`