feat(server): transcode bitrate and thread settings (#2488)

* support for two-pass transcoding

* added max bitrate and thread to transcode api

* admin page setting desc+bitrate and thread options

* Update web/src/lib/components/admin-page/settings/setting-input-field.svelte

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* Update web/src/lib/components/admin-page/settings/setting-input-field.svelte

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>

* two-pass slider, `crf` and `threads` as numbers

* updated and added transcode tests

* refactored `getFfmpegOptions`

* default `threads`, `maxBitrate` now 0, more tests

* vp9 constant quality mode

* fixed nullable `crf` and `threads`

* fixed two-pass slider, added apiproperty

* optional `desc` for `SettingSelect`

* disable two-pass if settings are incompatible

* fixed test

* transcode interface

---------

Co-authored-by: Michel Heusschen <59014050+michelheusschen@users.noreply.github.com>
This commit is contained in:
Mert
2023-05-22 14:07:43 -04:00
committed by GitHub
parent f1384fea58
commit e9722710ac
19 changed files with 498 additions and 46 deletions

View File

@@ -7,6 +7,7 @@
import SettingButtonsRow from '../setting-buttons-row.svelte';
import SettingInputField, { SettingInputFieldType } from '../setting-input-field.svelte';
import SettingSelect from '../setting-select.svelte';
import SettingSwitch from '../setting-switch.svelte';
import { isEqual } from 'lodash-es';
import { fade } from 'svelte/transition';
@@ -80,21 +81,34 @@
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
label="CONSTANT RATE FACTOR (-crf)"
desc="Video quality level. Typical values are 23 for H.264, 28 for HEVC, and 31 for VP9. Lower is better, but takes longer to encode and produces larger files."
bind:value={ffmpegConfig.crf}
required={true}
isEdited={!(ffmpegConfig.crf == savedConfig.crf)}
/>
<SettingInputField
inputType={SettingInputFieldType.TEXT}
<SettingSelect
label="PRESET (-preset)"
desc="Compression speed. Slower presets produce smaller files, and increase quality when targeting a certain bitrate. VP9 ignores speeds above `faster`."
bind:value={ffmpegConfig.preset}
required={true}
name="preset"
options={[
{ value: 'ultrafast', text: 'ultrafast' },
{ value: 'superfast', text: 'superfast' },
{ value: 'veryfast', text: 'veryfast' },
{ value: 'faster', text: 'faster' },
{ value: 'fast', text: 'fast' },
{ value: 'medium', text: 'medium' },
{ value: 'slow', text: 'slow' },
{ value: 'slower', text: 'slower' },
{ value: 'veryslow', text: 'veryslow' }
]}
isEdited={!(ffmpegConfig.preset == savedConfig.preset)}
/>
<SettingSelect
label="AUDIO CODEC"
desc="Opus is the highest quality option, but has lower compatibility with old devices or software."
bind:value={ffmpegConfig.targetAudioCodec}
options={[
{ value: 'aac', text: 'aac' },
@@ -107,6 +121,7 @@
<SettingSelect
label="VIDEO CODEC"
desc="VP9 has high efficiency and web compatibility, but takes longer to transcode. HEVC performs similarly, but has lower web compatibility. H.264 is widely compatible and quick to transcode, but produces much larger files."
bind:value={ffmpegConfig.targetVideoCodec}
options={[
{ value: 'h264', text: 'h264' },
@@ -119,6 +134,7 @@
<SettingSelect
label="TARGET RESOLUTION"
desc="Higher resolutions can preserve more detail but take longer to encode, have larger file sizes, and can reduce app responsiveness."
bind:value={ffmpegConfig.targetResolution}
options={[
{ value: '2160', text: '4k' },
@@ -131,8 +147,25 @@
isEdited={!(ffmpegConfig.targetResolution == savedConfig.targetResolution)}
/>
<SettingInputField
inputType={SettingInputFieldType.TEXT}
label="MAX BITRATE"
desc="Setting a max bitrate can make file sizes more predictable at a minor cost to quality. At 720p, typical values are 2600k for VP9 or HEVC, or 4500k for H.264. Disabled if set to 0."
bind:value={ffmpegConfig.maxBitrate}
isEdited={!(ffmpegConfig.maxBitrate == savedConfig.maxBitrate)}
/>
<SettingInputField
inputType={SettingInputFieldType.NUMBER}
label="THREADS"
desc="Higher values lead to faster encoding, but leave less room for the server to process other tasks while active. This value should not be more than the number of CPU cores. Maximizes utilization if set to 0."
bind:value={ffmpegConfig.threads}
isEdited={!(ffmpegConfig.threads == savedConfig.threads)}
/>
<SettingSelect
label="TRANSCODE"
desc="Policy for when a video should be transcoded."
bind:value={ffmpegConfig.transcode}
name="transcode"
options={[
@@ -152,6 +185,13 @@
]}
isEdited={!(ffmpegConfig.transcode == savedConfig.transcode)}
/>
<SettingSwitch
title="TWO-PASS ENCODING"
subtitle="Transcode in two passes to produce better encoded videos. When max bitrate is enabled (required for it to work with H.264 and HEVC), this mode uses a bitrate range based on the max bitrate and ignores CRF. For VP9, CRF can be used if max bitrate is disabled."
bind:checked={ffmpegConfig.twoPass}
isEdited={!(ffmpegConfig.twoPass === savedConfig.twoPass)}
/>
</div>
<div class="ml-4">

View File

@@ -12,8 +12,9 @@
import { fly } from 'svelte/transition';
export let inputType: SettingInputFieldType;
export let value: string;
export let value: string | number;
export let label = '';
export let desc = '';
export let required = false;
export let disabled = false;
export let isEdited = false;
@@ -39,8 +40,17 @@
</div>
{/if}
</div>
{#if desc}
<p class="immich-form-label text-xs pb-2" id="{label}-desc">
{desc}
</p>
{/if}
<input
class="immich-form-input w-full"
class="immich-form-input pb-2 w-full"
aria-describedby={desc ? `${label}-desc` : undefined}
aria-labelledby="{label}-label"
id={label}
name={label}
type={inputType}

View File

@@ -5,6 +5,7 @@
export let value: string;
export let options: { value: string; text: string }[];
export let label = '';
export let desc = '';
export let name = '';
export let isEdited = false;
@@ -26,8 +27,16 @@
</div>
{/if}
</div>
{#if desc}
<p class="immich-form-label text-xs pb-2" id="{name}-desc">
{desc}
</p>
{/if}
<select
class="immich-form-input w-full"
class="immich-form-input pb-2 w-full"
aria-describedby={desc ? `${name}-desc` : undefined}
{name}
id="{name}-select"
bind:value

View File

@@ -28,7 +28,7 @@
<p class="text-sm dark:text-immich-dark-fg">{subtitle}</p>
</div>
<label class="relative inline-block w-[36px] h-[10px]">
<label class="relative inline-block flex-none w-[36px] h-[10px]">
<input
class="opacity-0 w-0 h-0 disabled::cursor-not-allowed"
type="checkbox"