@@ -59,19 +59,16 @@ encoders supported by your FFmpeg install will show up.
|
|||||||
- [x] libx264rgb (Untested, but _should_ work)
|
- [x] libx264rgb (Untested, but _should_ work)
|
||||||
- [ ] h264_amf
|
- [ ] h264_amf
|
||||||
- [ ] h264_nvenc
|
- [ ] h264_nvenc
|
||||||
- [ ] h264_qsv
|
- [x] h264_qsv
|
||||||
- [ ] h264_vaapi
|
- [ ] h264_vaapi
|
||||||
- [ ] h264_vulkan
|
- [ ] h264_vulkan
|
||||||
- [ ] H.265
|
- [ ] H.265
|
||||||
- [x] libx265
|
- [x] libx265
|
||||||
- [ ] h264_amf
|
- [ ] h265_amf
|
||||||
- [ ] h264_nvenc
|
- [ ] h265_nvenc
|
||||||
- [ ] h264_qsv
|
- [x] h265_qsv
|
||||||
- [ ] h264_vaapi
|
- [ ] h265_vaapi
|
||||||
- [ ] h264_vulkan
|
- [ ] h265_vulkan
|
||||||
- [ ] VP8
|
|
||||||
- [ ] libvpx
|
|
||||||
- [ ] vp8_vaapi
|
|
||||||
- [ ] VP9
|
- [ ] VP9
|
||||||
- [ ] libvpx-vp9
|
- [ ] libvpx-vp9
|
||||||
- [ ] vp9_vaapi
|
- [ ] vp9_vaapi
|
||||||
|
|||||||
+22
-14
@@ -31,7 +31,7 @@ import BreezeIcon from "./components/BreezeIcon";
|
|||||||
import AV1Options from "./components/AV1Options";
|
import AV1Options from "./components/AV1Options";
|
||||||
import DNxHDOptions from "./components/DNxHDOptions";
|
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 {
|
interface RunningProcessInfo {
|
||||||
process: Neutralino.SpawnedProcess;
|
process: Neutralino.SpawnedProcess;
|
||||||
@@ -212,27 +212,25 @@ function App() {
|
|||||||
(v) => v.shortName === newValue,
|
(v) => v.shortName === newValue,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (newValue !== "h264" && newValue !== "hevc") {
|
let encoder = newValue;
|
||||||
ffmpegParams.twopass = false;
|
if (codecObj?.encoders.length !== 0) {
|
||||||
|
encoder = codecObj?.encoders[0] ?? "";
|
||||||
}
|
}
|
||||||
|
setSelectedCodec(codecObj);
|
||||||
|
setSelectedEncoder(encoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
createEffect(() => {
|
||||||
ffmpegParams = {
|
ffmpegParams = {
|
||||||
vcodec: codecObj?.shortName ?? "",
|
vcodec: selectedCodec()?.shortName ?? "",
|
||||||
|
encoder: selectedEncoder(),
|
||||||
useropts: {
|
useropts: {
|
||||||
global: "",
|
global: "",
|
||||||
input: "",
|
input: "",
|
||||||
output: "",
|
output: "",
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
});
|
||||||
let encoder = newValue;
|
|
||||||
if (codecObj?.encoders.length !== 0) {
|
|
||||||
encoder = codecObj?.encoders[0] ?? "";
|
|
||||||
}
|
|
||||||
ffmpegParams.encoder = encoder;
|
|
||||||
setSelectedCodec(codecObj);
|
|
||||||
setSelectedEncoder(encoder);
|
|
||||||
}
|
|
||||||
|
|
||||||
function getAudioEncoders() {
|
function getAudioEncoders() {
|
||||||
const codec = audioCodec();
|
const codec = audioCodec();
|
||||||
@@ -581,7 +579,16 @@ function App() {
|
|||||||
</For>
|
</For>
|
||||||
</select>
|
</select>
|
||||||
</form>
|
</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
|
<Match
|
||||||
when={
|
when={
|
||||||
selectedCodec()?.shortName === "h264" ||
|
selectedCodec()?.shortName === "h264" ||
|
||||||
@@ -590,6 +597,7 @@ function App() {
|
|||||||
>
|
>
|
||||||
<H264Options
|
<H264Options
|
||||||
codec={selectedCodec()}
|
codec={selectedCodec()}
|
||||||
|
encoder={selectedEncoder()}
|
||||||
params={ffmpegParams}
|
params={ffmpegParams}
|
||||||
onParamChanged={onParametersChanged}
|
onParamChanged={onParametersChanged}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -15,43 +15,36 @@ function DNxHDOptions(props: {
|
|||||||
onParamChanged: FFmpegParamChangedFunc;
|
onParamChanged: FFmpegParamChangedFunc;
|
||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<section id="commonLossyOptions">
|
<section id="commonLossyOptions" class="k-form">
|
||||||
<div class="row flex-col align-items-center">
|
<label>Help</label>
|
||||||
<h3 class="k-form-section-title">Encoder Options</h3>
|
<div>
|
||||||
</div>
|
<button
|
||||||
<div class="k-form">
|
class="icon-button"
|
||||||
<label>Help</label>
|
onclick={() => os.open("https://askubuntu.com/a/907515")}
|
||||||
<div>
|
title="DNxHD is a picky encoder."
|
||||||
<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,
|
|
||||||
});
|
|
||||||
}}
|
|
||||||
>
|
>
|
||||||
<option value="dnxhd">DNxHD</option>
|
<BreezeIcon icon="help-about" alt="Help" />
|
||||||
<option value="dnxhr_444">DNxHR 444</option>
|
</button>
|
||||||
<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>
|
|
||||||
</div>
|
</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>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,170 +1,40 @@
|
|||||||
import { createSignal, Show } from "solid-js";
|
import { Match, Switch } from "solid-js";
|
||||||
import {
|
import {
|
||||||
DEFAULT_BITRATE,
|
|
||||||
type CodecInfo,
|
type CodecInfo,
|
||||||
type FFmpegParamChangedFunc,
|
type FFmpegParamChangedFunc,
|
||||||
type FFmpegParams,
|
type FFmpegParams,
|
||||||
} from "../util/ffmpeg";
|
} from "../util/ffmpeg";
|
||||||
import { os } from "@neutralinojs/lib";
|
import LibH26xOptions from "./encoders/libx264";
|
||||||
import BreezeIcon from "./BreezeIcon";
|
import H264QsvOptions from "./encoders/h264qsv";
|
||||||
|
|
||||||
const information = {
|
|
||||||
h264: {
|
|
||||||
defaultCrf: 23,
|
|
||||||
},
|
|
||||||
hevc: {
|
|
||||||
defaultCrf: 28,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Options for H.264/H.265 codecs
|
* Options for H.264/H.265 codecs
|
||||||
*/
|
*/
|
||||||
function H264Options(props: {
|
function H264Options(props: {
|
||||||
codec: CodecInfo | undefined;
|
codec: CodecInfo | undefined;
|
||||||
|
encoder: string;
|
||||||
params: FFmpegParams;
|
params: FFmpegParams;
|
||||||
onParamChanged: FFmpegParamChangedFunc;
|
onParamChanged: FFmpegParamChangedFunc;
|
||||||
}) {
|
}) {
|
||||||
const [twopass, setTwopass] = createSignal(false);
|
|
||||||
const defaultCrf =
|
|
||||||
props.codec?.shortName === "h264"
|
|
||||||
? information.h264.defaultCrf
|
|
||||||
: information.hevc.defaultCrf;
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="commonLossyOptions">
|
<Switch fallback={<div class="text-center mt-4">No options.</div>}>
|
||||||
<div class="row flex-col align-items-center">
|
<Match
|
||||||
<h3 class="k-form-section-title">Encoder Options</h3>
|
when={
|
||||||
</div>
|
props.encoder === "libx264" ||
|
||||||
<div class="k-form">
|
props.encoder === "libx264rgb" ||
|
||||||
<div></div>
|
props.encoder === "libx265"
|
||||||
<div class="checkbox-container">
|
}
|
||||||
<input
|
>
|
||||||
type="checkbox"
|
<LibH26xOptions {...props} />
|
||||||
value={props.params.twopass?.toString()}
|
</Match>
|
||||||
onInput={(e) => {
|
<Match
|
||||||
props.params.twopass = e.target.checked;
|
when={
|
||||||
props.onParamChanged("twopass", e.target.checked);
|
props.encoder === "h264_qsv" || props.encoder === "hevc_qsv"
|
||||||
setTwopass(e.target.checked);
|
}
|
||||||
}}
|
>
|
||||||
id="twopassCheck"
|
<H264QsvOptions {...props} />
|
||||||
/>
|
</Match>
|
||||||
<label for="twopassCheck">
|
</Switch>
|
||||||
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>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,138 +16,128 @@ function VP9Options(props: {
|
|||||||
const defaultCrf = 30;
|
const defaultCrf = 30;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="commonLossyOptions">
|
<section id="commonLossyOptions" class="k-form">
|
||||||
<div class="row flex-col align-items-center">
|
<div></div>
|
||||||
<h3 class="k-form-section-title">Encoder Options</h3>
|
<div class="checkbox-container">
|
||||||
</div>
|
<input
|
||||||
<div class="k-form">
|
type="checkbox"
|
||||||
<div></div>
|
value={props.params.twopass?.toString()}
|
||||||
<div class="checkbox-container">
|
onInput={(e) => {
|
||||||
<input
|
props.params.twopass = e.target.checked;
|
||||||
type="checkbox"
|
props.onParamChanged("twopass", e.target.checked);
|
||||||
value={props.params.twopass?.toString()}
|
setTwopass(e.target.checked);
|
||||||
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);
|
|
||||||
}}
|
}}
|
||||||
>
|
id="twopassCheck"
|
||||||
<option value="ultrafast">ultrafast</option>
|
/>
|
||||||
<option value="superfast">superfast</option>
|
<label for="twopassCheck">
|
||||||
<option value="veryfast">veryfast</option>
|
Use target bitrate instead of CRF
|
||||||
<option value="faster">faster</option>
|
</label>
|
||||||
<option value="fast">fast</option>
|
<button
|
||||||
<option value="medium">medium (Default)</option>
|
class="icon-button"
|
||||||
<option value="slow">slow</option>
|
onclick={() =>
|
||||||
<option value="slower">slower</option>
|
os.open(
|
||||||
<option value="veryslow">veryslow</option>
|
"https://trac.ffmpeg.org/wiki/Encode/H.264#twopass",
|
||||||
<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),
|
|
||||||
);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
}
|
}
|
||||||
|
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>
|
<BreezeIcon icon="help-about" alt="Help" />
|
||||||
<div>
|
</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
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="bitrate"
|
name="crf"
|
||||||
id="bitrate"
|
id="crf"
|
||||||
value={props.params.vbitrate ?? DEFAULT_BITRATE}
|
min="0"
|
||||||
|
max="51"
|
||||||
|
value={props.params.crf ?? defaultCrf}
|
||||||
oninput={(e) => {
|
oninput={(e) => {
|
||||||
props.params.vbitrate = parseInt(
|
props.params.crf = parseInt(e.target.value);
|
||||||
e.target.value,
|
|
||||||
);
|
|
||||||
props.onParamChanged(
|
props.onParamChanged(
|
||||||
"vbitrate",
|
"crf",
|
||||||
parseInt(e.target.value),
|
parseInt(e.target.value),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<span> Kbps</span>
|
</>
|
||||||
</div>
|
}
|
||||||
</Show>
|
>
|
||||||
<Show when={props.codec?.shortName === "h264"}>
|
<label for="bitrate">Bitrate</label>
|
||||||
<div></div>
|
<div>
|
||||||
<div class="checkbox-container">
|
<input
|
||||||
<input
|
type="number"
|
||||||
type="checkbox"
|
name="bitrate"
|
||||||
value={props.params.faststart?.toString()}
|
id="bitrate"
|
||||||
onInput={(e) => {
|
value={props.params.vbitrate ?? DEFAULT_BITRATE}
|
||||||
props.params.faststart = e.target.checked;
|
oninput={(e) => {
|
||||||
props.onParamChanged(
|
props.params.vbitrate = parseInt(e.target.value);
|
||||||
"faststart",
|
props.onParamChanged(
|
||||||
e.target.checked,
|
"vbitrate",
|
||||||
);
|
parseInt(e.target.value),
|
||||||
}}
|
);
|
||||||
id="fastStartCheck"
|
}}
|
||||||
/>
|
/>
|
||||||
<label for="fastStartCheck">Enable Fast Start</label>
|
<span> Kbps</span>
|
||||||
<button
|
</div>
|
||||||
class="icon-button"
|
</Show>
|
||||||
onclick={() =>
|
<Show when={props.codec?.shortName === "h264"}>
|
||||||
os.open(
|
<div></div>
|
||||||
"https://trac.ffmpeg.org/wiki/Encode/H.264#faststartforwebvideo",
|
<div class="checkbox-container">
|
||||||
)
|
<input
|
||||||
}
|
type="checkbox"
|
||||||
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."
|
value={props.params.faststart?.toString()}
|
||||||
>
|
onInput={(e) => {
|
||||||
<BreezeIcon icon="help-about" alt="Help" />
|
props.params.faststart = e.target.checked;
|
||||||
</button>
|
props.onParamChanged("faststart", e.target.checked);
|
||||||
</div>
|
}}
|
||||||
</Show>
|
id="fastStartCheck"
|
||||||
</div>
|
/>
|
||||||
|
<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>
|
</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;
|
||||||
@@ -46,79 +46,71 @@ function LibaomOptions(props: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="encoderOptions">
|
<section id="encoderOptions" class="k-form">
|
||||||
<div class="row flex-col align-items-center">
|
<label>Help</label>
|
||||||
<h3 class="k-form-section-title">Encoder Options</h3>
|
<div>
|
||||||
</div>
|
<button
|
||||||
<div class="k-form">
|
class="icon-button"
|
||||||
<label>Help</label>
|
onclick={() =>
|
||||||
<div>
|
os.open(
|
||||||
<button
|
"https://trac.ffmpeg.org/wiki/Encode/AV1#libaom",
|
||||||
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"
|
|
||||||
}
|
}
|
||||||
|
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
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="crf"
|
name="bitrate"
|
||||||
id="crf"
|
id="bitrate"
|
||||||
min="1"
|
aria-label="Kbps"
|
||||||
max="63"
|
value={props.params.vbitrate ?? DEFAULT_BITRATE}
|
||||||
value={props.params.crf ?? DEFAULT_CRF}
|
|
||||||
oninput={(e) => {
|
oninput={(e) => {
|
||||||
props.onParamChanged(
|
props.onParamChanged(
|
||||||
"crf",
|
"vbitrate",
|
||||||
parseInt(e.target.value),
|
parseInt(e.target.value),
|
||||||
);
|
);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</Show>
|
<span>Kbps</span>
|
||||||
<Show when={rateControlMode() !== "Constant"}>
|
</div>
|
||||||
<label for="bitrate">Bitrate</label>
|
</Show>
|
||||||
<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>
|
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ function Librav1eOptions(props: {
|
|||||||
onParamChanged: FFmpegParamChangedFunc;
|
onParamChanged: FFmpegParamChangedFunc;
|
||||||
}) {
|
}) {
|
||||||
onMount(() => {
|
onMount(() => {
|
||||||
props.onParamChanged("crf", undefined);
|
|
||||||
props.onParamChanged(
|
props.onParamChanged(
|
||||||
"vbitrate",
|
"vbitrate",
|
||||||
props.params.vbitrate ?? DEFAULT_BITRATE,
|
props.params.vbitrate ?? DEFAULT_BITRATE,
|
||||||
@@ -23,53 +22,48 @@ function Librav1eOptions(props: {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="encoderOptions">
|
<section id="encoderOptions" class="k-form">
|
||||||
<div class="row flex-col align-items-center">
|
<label>Help</label>
|
||||||
<h3 class="k-form-section-title">Encoder Options</h3>
|
<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>
|
||||||
<div class="k-form">
|
<label>Speed</label>
|
||||||
<label>Help</label>
|
<input
|
||||||
<div>
|
type="number"
|
||||||
<button
|
name="speed"
|
||||||
class="icon-button"
|
id="speed"
|
||||||
onclick={() =>
|
min="0"
|
||||||
os.open(
|
max="10"
|
||||||
"https://www.ffmpeg.org/ffmpeg-all.html#librav1e",
|
value={props.params.speed ?? 5}
|
||||||
)
|
oninput={(e) =>
|
||||||
}
|
props.onParamChanged("speed", parseInt(e.target.value))
|
||||||
title="Click to view the documentation for this encoder."
|
}
|
||||||
>
|
/>
|
||||||
<BreezeIcon icon="help-about" alt="Help" />
|
<label for="bitrate">Bitrate</label>
|
||||||
</button>
|
<div class="row gap2 align-items-center">
|
||||||
</div>
|
|
||||||
<label>Speed</label>
|
|
||||||
<input
|
<input
|
||||||
type="number"
|
type="number"
|
||||||
name="speed"
|
name="bitrate"
|
||||||
id="speed"
|
id="bitrate"
|
||||||
min="0"
|
value={props.params.vbitrate ?? DEFAULT_BITRATE}
|
||||||
max="10"
|
|
||||||
value={props.params.speed ?? 5}
|
|
||||||
oninput={(e) =>
|
oninput={(e) =>
|
||||||
props.onParamChanged("speed", parseInt(e.target.value))
|
props.onParamChanged(
|
||||||
|
"vbitrate",
|
||||||
|
parseInt(e.target.value),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<label for="bitrate">Bitrate</label>
|
<span>Kbps</span>
|
||||||
<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>
|
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -33,90 +33,75 @@ function LibSvtAv1Options({
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
onMount(() => {
|
|
||||||
onParamChanged("vbitrate", undefined);
|
|
||||||
|
|
||||||
if (isNaN(parseInt(params.preset ?? ""))) {
|
|
||||||
onParamChanged("preset", "5");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<section id="encoderOptions">
|
<section id="encoderOptions" class="k-form">
|
||||||
<div class="row flex-col align-items-center">
|
<label>Help</label>
|
||||||
<h3 class="k-form-section-title">Encoder Options</h3>
|
<div>
|
||||||
</div>
|
<button
|
||||||
<div class="k-form">
|
class="icon-button"
|
||||||
<label>Help</label>
|
onclick={() =>
|
||||||
<div>
|
os.open(
|
||||||
<button
|
"https://gitlab.com/AOMediaCodec/SVT-AV1/-/blob/master/Docs/Ffmpeg.md",
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
min="1"
|
title="Click to view the documentation for this encoder."
|
||||||
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>
|
<BreezeIcon icon="help-about" alt="Help" />
|
||||||
<option value="1">Objective Quality (PSNR)</option>
|
</button>
|
||||||
</select>
|
|
||||||
</div>
|
</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>
|
</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;
|
||||||
@@ -171,7 +171,7 @@ export function generateOutputCommand(params: FFmpegParams) {
|
|||||||
? " -movflags +faststart"
|
? " -movflags +faststart"
|
||||||
: "";
|
: "";
|
||||||
|
|
||||||
let globalopts = "-hwaccel auto -y";
|
let globalopts = `-hwaccel ${params.hwaccel ?? "auto"} -y`;
|
||||||
let inputopts =
|
let inputopts =
|
||||||
params.useropts.input !== "" ? " " + params.useropts.input : "";
|
params.useropts.input !== "" ? " " + params.useropts.input : "";
|
||||||
let outputopts =
|
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}`
|
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}`
|
}${faststart}${params.preset === undefined ? "" : ` -preset ${params.preset}`
|
||||||
} -c:a ${params.acodec ?? "copy"}${params.abitrate === undefined ? "" : ` -b:a ${params.abitrate}k`
|
} -c:a ${params.acodec ?? "copy"}${params.abitrate === undefined ? "" : ` -b:a ${params.abitrate}k`
|
||||||
}${params.speed === undefined ? "" : ` -speed ${params.speed}`
|
}${params.speed === undefined ? "" : ` -speed ${params.speed}`
|
||||||
|
|||||||
Reference in New Issue
Block a user