1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-28 22:51:59 +00:00

use old filters for cpu and when vulkan gpu is not available

This commit is contained in:
mertalev 2024-05-12 22:47:19 -04:00
parent 86f113cc96
commit 9ffc2e59ef
No known key found for this signature in database
GPG key ID: 13C97EF14A338984
6 changed files with 250 additions and 108 deletions

View file

@ -58,6 +58,7 @@
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"sharp": "^0.33.0", "sharp": "^0.33.0",
"shelljs": "^0.8.5",
"sirv": "^2.0.4", "sirv": "^2.0.4",
"thumbhash": "^0.1.1", "thumbhash": "^0.1.1",
"typeorm": "^0.3.17", "typeorm": "^0.3.17",
@ -83,6 +84,7 @@
"@types/node": "^20.5.7", "@types/node": "^20.5.7",
"@types/nodemailer": "^6.4.14", "@types/nodemailer": "^6.4.14",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/shelljs": "^0.8.15",
"@types/supertest": "^6.0.0", "@types/supertest": "^6.0.0",
"@types/ua-parser-js": "^0.7.36", "@types/ua-parser-js": "^0.7.36",
"@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0",
@ -5741,6 +5743,16 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
"dev": true,
"dependencies": {
"@types/minimatch": "*",
"@types/node": "*"
}
},
"node_modules/@types/http-assert": { "node_modules/@types/http-assert": {
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz",
@ -5847,6 +5859,12 @@
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==" "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
}, },
"node_modules/@types/minimatch": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
"dev": true
},
"node_modules/@types/mock-fs": { "node_modules/@types/mock-fs": {
"version": "4.13.4", "version": "4.13.4",
"resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz", "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz",
@ -6043,6 +6061,16 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"node_modules/@types/shelljs": {
"version": "0.8.15",
"resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.15.tgz",
"integrity": "sha512-vzmnCHl6hViPu9GNLQJ+DZFd6BQI2DBTUeOvYHqkWQLMfKAAQYMb/xAmZkTogZI/vqXHCWkqDRymDI5p0QTi5Q==",
"dev": true,
"dependencies": {
"@types/glob": "~7.2.0",
"@types/node": "*"
}
},
"node_modules/@types/shimmer": { "node_modules/@types/shimmer": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz",
@ -20045,6 +20073,16 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/glob": {
"version": "7.2.0",
"resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz",
"integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==",
"dev": true,
"requires": {
"@types/minimatch": "*",
"@types/node": "*"
}
},
"@types/http-assert": { "@types/http-assert": {
"version": "1.5.5", "version": "1.5.5",
"resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz", "resolved": "https://registry.npmjs.org/@types/http-assert/-/http-assert-1.5.5.tgz",
@ -20151,6 +20189,12 @@
"resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz", "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.3.tgz",
"integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg==" "integrity": "sha512-Ys+/St+2VF4+xuY6+kDIXGxbNRO0mesVg0bbxEfB97Od1Vjpjx9KD1qxs64Gcb3CWPirk9Xe+PT4YiiHQ9T+eg=="
}, },
"@types/minimatch": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz",
"integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==",
"dev": true
},
"@types/mock-fs": { "@types/mock-fs": {
"version": "4.13.4", "version": "4.13.4",
"resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz", "resolved": "https://registry.npmjs.org/@types/mock-fs/-/mock-fs-4.13.4.tgz",
@ -20334,6 +20378,16 @@
"@types/node": "*" "@types/node": "*"
} }
}, },
"@types/shelljs": {
"version": "0.8.15",
"resolved": "https://registry.npmjs.org/@types/shelljs/-/shelljs-0.8.15.tgz",
"integrity": "sha512-vzmnCHl6hViPu9GNLQJ+DZFd6BQI2DBTUeOvYHqkWQLMfKAAQYMb/xAmZkTogZI/vqXHCWkqDRymDI5p0QTi5Q==",
"dev": true,
"requires": {
"@types/glob": "~7.2.0",
"@types/node": "*"
}
},
"@types/shimmer": { "@types/shimmer": {
"version": "1.0.5", "version": "1.0.5",
"resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz", "resolved": "https://registry.npmjs.org/@types/shimmer/-/shimmer-1.0.5.tgz",

View file

@ -82,6 +82,7 @@
"rxjs": "^7.8.1", "rxjs": "^7.8.1",
"sanitize-filename": "^1.6.3", "sanitize-filename": "^1.6.3",
"sharp": "^0.33.0", "sharp": "^0.33.0",
"shelljs": "^0.8.5",
"sirv": "^2.0.4", "sirv": "^2.0.4",
"thumbhash": "^0.1.1", "thumbhash": "^0.1.1",
"typeorm": "^0.3.17", "typeorm": "^0.3.17",
@ -107,6 +108,7 @@
"@types/node": "^20.5.7", "@types/node": "^20.5.7",
"@types/nodemailer": "^6.4.14", "@types/nodemailer": "^6.4.14",
"@types/picomatch": "^2.3.3", "@types/picomatch": "^2.3.3",
"@types/shelljs": "^0.8.15",
"@types/supertest": "^6.0.0", "@types/supertest": "^6.0.0",
"@types/ua-parser-js": "^0.7.36", "@types/ua-parser-js": "^0.7.36",
"@typescript-eslint/eslint-plugin": "^7.0.0", "@typescript-eslint/eslint-plugin": "^7.0.0",

View file

@ -66,6 +66,25 @@ export interface BitrateDistribution {
unit: string; unit: string;
} }
export enum VulkanDeviceType {
OTHER = 'OTHER',
INTEGRATED_GPU = 'INTEGRATED_GPU',
DISCRETE_GPU = 'DISCRETE_GPU',
VIRTUAL_GPU = 'VIRTUAL_GPU',
CPU = 'CPU',
}
export interface VulkanDevice {
index: number;
type: VulkanDeviceType;
}
export interface DeviceSummary {
driDevices: string[];
hasOpenCL: boolean;
vulkanDevices: VulkanDevice[];
}
export interface VideoCodecSWConfig { export interface VideoCodecSWConfig {
getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream: AudioStreamInfo): TranscodeOptions; getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream: AudioStreamInfo): TranscodeOptions;
} }
@ -84,4 +103,5 @@ export interface IMediaRepository {
// video // video
probe(input: string): Promise<VideoInfo>; probe(input: string): Promise<VideoInfo>;
transcode(input: string, output: string | Writable, options: TranscodeOptions): Promise<void>; transcode(input: string, output: string | Writable, options: TranscodeOptions): Promise<void>;
getVulkanDevices(): Promise<VulkanDevice[]>;
} }

View file

@ -5,6 +5,7 @@ import fs from 'node:fs/promises';
import { Writable } from 'node:stream'; import { Writable } from 'node:stream';
import { promisify } from 'node:util'; import { promisify } from 'node:util';
import sharp from 'sharp'; import sharp from 'sharp';
import shell, { ShellString } from 'shelljs';
import { Colorspace } from 'src/entities/system-config.entity'; import { Colorspace } from 'src/entities/system-config.entity';
import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { import {
@ -13,11 +14,14 @@ import {
ThumbnailOptions, ThumbnailOptions,
TranscodeOptions, TranscodeOptions,
VideoInfo, VideoInfo,
VulkanDevice,
VulkanDeviceType,
} from 'src/interfaces/media.interface'; } from 'src/interfaces/media.interface';
import { Instrumentation } from 'src/utils/instrumentation'; import { Instrumentation } from 'src/utils/instrumentation';
import { handlePromiseError } from 'src/utils/misc'; import { handlePromiseError } from 'src/utils/misc';
const probe = promisify<string, FfprobeData>(ffmpeg.ffprobe); const probe = promisify<string, FfprobeData>(ffmpeg.ffprobe);
const exec = promisify<string, ShellString>(shell.exec);
sharp.concurrency(0); sharp.concurrency(0);
sharp.cache({ files: 0 }); sharp.cache({ files: 0 });
@ -150,6 +154,31 @@ export class MediaRepository implements IMediaRepository {
return { width, height }; return { width, height };
} }
async getVulkanDevices(): Promise<VulkanDevice[]> {
return [
{ index: 0, type: VulkanDeviceType.DISCRETE_GPU },
{ index: 1, type: VulkanDeviceType.CPU },
];
const devices = [];
let i = 0;
while (true) {
try {
const vulkanInfo = JSON.parse(await exec(`vulkaninfo --json=${i} -o /dev/tty`));
const deviceType =
vulkanInfo['capabilities']['device']['properties']['VkPhysicalDeviceProperties']['deviceType'];
devices.push({
index: i,
type: deviceType.replace('VK_PHYSICAL_DEVICE_TYPE_', ''),
});
i++;
} catch {
break;
}
}
return devices;
}
private configureFfmpegCall(input: string, output: string | Writable, options: TranscodeOptions) { private configureFfmpegCall(input: string, output: string | Writable, options: TranscodeOptions) {
return ffmpeg(input, { niceness: 10 }) return ffmpeg(input, { niceness: 10 })
.inputOptions(options.inputOptions) .inputOptions(options.inputOptions)

View file

@ -27,7 +27,13 @@ import {
QueueName, QueueName,
} from 'src/interfaces/job.interface'; } from 'src/interfaces/job.interface';
import { ILoggerRepository } from 'src/interfaces/logger.interface'; import { ILoggerRepository } from 'src/interfaces/logger.interface';
import { AudioStreamInfo, IMediaRepository, VideoCodecHWConfig, VideoStreamInfo } from 'src/interfaces/media.interface'; import {
AudioStreamInfo,
DeviceSummary,
IMediaRepository,
VideoCodecHWConfig,
VideoStreamInfo,
} from 'src/interfaces/media.interface';
import { IMoveRepository } from 'src/interfaces/move.interface'; import { IMoveRepository } from 'src/interfaces/move.interface';
import { IPersonRepository } from 'src/interfaces/person.interface'; import { IPersonRepository } from 'src/interfaces/person.interface';
import { IStorageRepository } from 'src/interfaces/storage.interface'; import { IStorageRepository } from 'src/interfaces/storage.interface';
@ -50,8 +56,7 @@ import { usePagination } from 'src/utils/pagination';
export class MediaService { export class MediaService {
private configCore: SystemConfigCore; private configCore: SystemConfigCore;
private storageCore: StorageCore; private storageCore: StorageCore;
private openCL: boolean | null = null; private deviceSummary: DeviceSummary | null = null;
private devices: string[] | null = null;
constructor( constructor(
@Inject(IAssetRepository) private assetRepository: IAssetRepository, @Inject(IAssetRepository) private assetRepository: IAssetRepository,
@ -492,22 +497,23 @@ export class MediaService {
} }
private async getHWCodecConfig(config: SystemConfigFFmpegDto) { private async getHWCodecConfig(config: SystemConfigFFmpegDto) {
const deviceSummary = await this.getDeviceSummary();
let handler: VideoCodecHWConfig; let handler: VideoCodecHWConfig;
switch (config.accel) { switch (config.accel) {
case TranscodeHWAccel.NVENC: { case TranscodeHWAccel.NVENC: {
handler = new NVENCConfig(config); handler = new NVENCConfig(config, deviceSummary);
break; break;
} }
case TranscodeHWAccel.QSV: { case TranscodeHWAccel.QSV: {
handler = new QSVConfig(config, await this.getDevices()); handler = new QSVConfig(config, deviceSummary);
break; break;
} }
case TranscodeHWAccel.VAAPI: { case TranscodeHWAccel.VAAPI: {
handler = new VAAPIConfig(config, await this.getDevices()); handler = new VAAPIConfig(config, deviceSummary);
break; break;
} }
case TranscodeHWAccel.RKMPP: { case TranscodeHWAccel.RKMPP: {
handler = new RKMPPConfig(config, await this.getDevices()); handler = new RKMPPConfig(config, deviceSummary);
break; break;
} }
default: { default: {
@ -559,26 +565,42 @@ export class MediaService {
return extractedSize >= targetSize; return extractedSize >= targetSize;
} }
private async getDevices() { private async getDeviceSummary(): Promise<DeviceSummary> {
if (!this.devices) { if (!this.deviceSummary) {
this.devices = await this.storageRepository.readdir('/dev/dri'); this.deviceSummary = {
driDevices: await this.getDriDevices(),
hasOpenCL: await this.hasOpenCL(),
vulkanDevices: await this.mediaRepository.getVulkanDevices(),
};
} }
return this.devices; return this.deviceSummary;
}
private async getDriDevices() {
const devices = await this.storageRepository.readdir('/dev/dri');
return devices
.filter((device) => device.startsWith('renderD') || device.startsWith('card'))
.sort((a, b) => {
// order GPU devices first
if (a.startsWith('card') && b.startsWith('renderD')) {
return -1;
}
if (a.startsWith('renderD') && b.startsWith('card')) {
return 1;
}
return -a.localeCompare(b);
});
} }
private async hasOpenCL() { private async hasOpenCL() {
if (this.openCL === null) { try {
try { const maliIcdStat = await this.storageRepository.stat('/etc/OpenCL/vendors/mali.icd');
const maliIcdStat = await this.storageRepository.stat('/etc/OpenCL/vendors/mali.icd'); const maliDeviceStat = await this.storageRepository.stat('/dev/mali0');
const maliDeviceStat = await this.storageRepository.stat('/dev/mali0'); return maliIcdStat.isFile() && maliDeviceStat.isCharacterDevice();
this.openCL = maliIcdStat.isFile() && maliDeviceStat.isCharacterDevice(); } catch {
} catch { this.logger.warn('OpenCL not available for transcoding, using CPU instead.');
this.logger.warn('OpenCL not available for transcoding, using CPU instead.'); return false;
this.openCL = false;
}
} }
return this.openCL;
} }
} }

View file

@ -3,10 +3,12 @@ import { CQMode, ToneMapping, TranscodeHWAccel, TranscodeTarget, VideoCodec } fr
import { import {
AudioStreamInfo, AudioStreamInfo,
BitrateDistribution, BitrateDistribution,
DeviceSummary,
TranscodeOptions, TranscodeOptions,
VideoCodecHWConfig, VideoCodecHWConfig,
VideoCodecSWConfig, VideoCodecSWConfig,
VideoStreamInfo, VideoStreamInfo,
VulkanDeviceType,
} from 'src/interfaces/media.interface'; } from 'src/interfaces/media.interface';
class BaseConfig implements VideoCodecSWConfig { class BaseConfig implements VideoCodecSWConfig {
@ -37,7 +39,7 @@ class BaseConfig implements VideoCodecSWConfig {
// eslint-disable-next-line @typescript-eslint/no-unused-vars // eslint-disable-next-line @typescript-eslint/no-unused-vars
getBaseInputOptions(videoStream: VideoStreamInfo): string[] { getBaseInputOptions(videoStream: VideoStreamInfo): string[] {
return [...this.getDeviceOptions(), ...this.getInputThreadOptions()]; return this.getInputThreadOptions();
} }
getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) { getBaseOutputOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo) {
@ -79,39 +81,20 @@ class BaseConfig implements VideoCodecSWConfig {
} }
getFilterOptions(videoStream: VideoStreamInfo) { getFilterOptions(videoStream: VideoStreamInfo) {
const options = ['hwupload=derive_device=vulkan']; const options = [];
if (this.shouldScale(videoStream)) { if (this.shouldScale(videoStream)) {
const { width, height } = this.getSize(videoStream); const { width, height } = this.getSize(videoStream);
options.push(`scale_vulkan=w=${width}:h=${height}`); options.push(`scale=w=${width}:h=${height}`);
} }
const colors = this.getColors(); if (this.shouldToneMap(videoStream)) {
const libplaceboOptions = [ options.push(...this.getToneMapping(videoStream));
'format=yuv420p',
'upscaler=none',
'downscaler=none',
`tonemapping=${this.shouldToneMap(videoStream) ? this.config.tonemap : 'clip'}`,
`colorspace=${colors.matrix}`,
`color_primaries=${colors.primaries}`,
`color_trc=${colors.transfer}`,
];
// use faster settings on cpu, nicer settings on gpu
if (this.config.accel === TranscodeHWAccel.DISABLED) {
libplaceboOptions.push('peak_detect=false');
} else {
libplaceboOptions.push('deband=true', 'deband_iterations=3', 'deband_radius=8', 'deband_threshold=6');
} }
options.push('format=yuv420p');
const libplacebo = `libplacebo=${libplaceboOptions.join(':')}`;
options.push(libplacebo, this.getFilterEnd(), 'format=yuv420p');
return options; return options;
} }
getFilterEnd(): string {
return 'hwdownload';
}
getPresetOptions() { getPresetOptions() {
return [`-preset ${this.config.preset}`]; return [`-preset ${this.config.preset}`];
} }
@ -243,34 +226,17 @@ class BaseConfig implements VideoCodecSWConfig {
} }
} }
getDeviceOptions() { getToneMapping(videoStream: VideoStreamInfo) {
return [ if (!this.shouldToneMap(videoStream)) {
`-init_hw_device ${this.getAccel()}=${this.getDevice()}`, return [];
`-filter_hw_device ${this.getAccel()}`,
`-hwaccel ${this.getAccel()}`,
`-hwaccel_output_format ${this.getOutputFormat()}`,
];
}
getDevice() {
let device = this.getAccel();
if (this.getDeviceSpecifier() !== null) {
device += `:${this.getDeviceSpecifier()}`;
} }
return device; const colors = this.getColors();
} return [
`zscale=t=linear:npl=${this.getNPL()}`,
getDeviceSpecifier(): string | null { `tonemap=${this.config.tonemap}:desat=0`,
return null; `zscale=p=${colors.primaries}:t=${colors.transfer}:m=${colors.matrix}:range=pc`,
} ];
getAccel() {
return 'vulkan';
}
getOutputFormat() {
return this.getAccel();
} }
getAudioCodec(): string { getAudioCodec(): string {
@ -299,35 +265,50 @@ class BaseConfig implements VideoCodecSWConfig {
} }
export class BaseHWConfig extends BaseConfig implements VideoCodecHWConfig { export class BaseHWConfig extends BaseConfig implements VideoCodecHWConfig {
protected devices: string[]; protected deviceSummary: DeviceSummary;
constructor( constructor(
protected config: SystemConfigFFmpegDto, protected config: SystemConfigFFmpegDto,
devices: string[] = [], deviceSummary: DeviceSummary,
) { ) {
super(config); super(config);
this.devices = this.validateDevices(devices); this.deviceSummary = deviceSummary;
}
getFilterOptions(videoStream: VideoStreamInfo) {
const options = ['hwupload=derive_device=vulkan'];
if (this.shouldScale(videoStream)) {
const { width, height } = this.getSize(videoStream);
options.push(`scale_vulkan=w=${width}:h=${height}`);
}
options.push(...this.getToneMapping(videoStream), `hwupload=derive_device=${this.getAccel()}`);
return options;
}
getToneMapping(videoStream: VideoStreamInfo) {
const colors = this.getColors();
const libplaceboOptions = [
`tonemapping=${this.shouldToneMap(videoStream) ? this.config.tonemap : 'clip'}`,
`colorspace=${colors.matrix}`,
`color_primaries=${colors.primaries}`,
`color_trc=${colors.transfer}`,
'format=yuv420p',
'deband=true',
'deband_iterations=3',
'deband_radius=8',
'deband_threshold=6',
'upscaler=none',
'downscaler=none',
];
return [`libplacebo=${libplaceboOptions.join(':')}`];
} }
getSupportedCodecs() { getSupportedCodecs() {
return [VideoCodec.H264, VideoCodec.HEVC]; return [VideoCodec.H264, VideoCodec.HEVC];
} }
validateDevices(devices: string[]) {
return devices
.filter((device) => device.startsWith('renderD') || device.startsWith('card'))
.sort((a, b) => {
// order GPU devices first
if (a.startsWith('card') && b.startsWith('renderD')) {
return -1;
}
if (a.startsWith('renderD') && b.startsWith('card')) {
return 1;
}
return -a.localeCompare(b);
});
}
getVideoCodec(): string { getVideoCodec(): string {
return `${this.config.targetVideoCodec}_${this.config.accel}`; return `${this.config.targetVideoCodec}_${this.config.accel}`;
} }
@ -346,23 +327,61 @@ export class BaseHWConfig extends BaseConfig implements VideoCodecHWConfig {
} }
const deviceName = device.replace('/dev/dri/', ''); const deviceName = device.replace('/dev/dri/', '');
if (!this.devices.includes(deviceName)) { if (!this.deviceSummary.driDevices.includes(deviceName)) {
throw new Error(`Device '${device}' does not exist`); throw new Error(`Device '${device}' does not exist`);
} }
return device; return device;
} }
// eslint-disable-next-line @typescript-eslint/no-unused-vars
getBaseInputOptions(videoStream: VideoStreamInfo): string[] {
return [...this.getDeviceOptions(), ...this.getInputThreadOptions()];
}
getInputThreadOptions() { getInputThreadOptions() {
return [`-threads ${this.config.threads <= 0 ? 1 : this.config.threads}`]; return [];
} }
getOutputThreadOptions() { getOutputThreadOptions() {
return []; return [];
} }
getFilterEnd(): string { getDeviceOptions() {
return `hwupload=derive_device=${this.getAccel()}`; return [
`-init_hw_device ${this.getAccel()}=${this.getDevice()}`,
`-filter_hw_device ${this.getAccel()}`,
`-hwaccel ${this.getAccel()}`,
`-hwaccel_output_format ${this.getOutputFormat()}`,
];
}
getDevice() {
let device = this.getAccel();
if (this.getDeviceSpecifier() !== null) {
device += `:${this.getDeviceSpecifier()}`;
}
return device;
}
getDeviceSpecifier(): string | null {
return null;
}
getAccel() {
if (!this.deviceSummary.vulkanDevices.some((device) => device.type !== VulkanDeviceType.CPU)) {
return 'vulkan';
}
return this.getPreferredAccel();
}
getPreferredAccel() {
return 'vulkan';
}
getOutputFormat() {
return this.getAccel();
} }
} }
@ -488,7 +507,7 @@ export class AV1Config extends BaseConfig {
} }
export class NVENCConfig extends BaseHWConfig { export class NVENCConfig extends BaseHWConfig {
getAccel() { getPreferredAccel() {
return 'cuda'; return 'cuda';
} }
@ -561,18 +580,18 @@ export class NVENCConfig extends BaseHWConfig {
} }
export class QSVConfig extends BaseHWConfig { export class QSVConfig extends BaseHWConfig {
getAccel() { getPreferredAccel() {
return 'qsv'; return 'qsv';
} }
getDeviceSpecifier() { getDeviceSpecifier() {
if (this.devices.length === 0) { if (this.deviceSummary.driDevices.length === 0) {
throw new Error('No VAAPI device found'); throw new Error('No VAAPI device found');
} }
let hwDevice = this.getPreferredDevice(); let hwDevice = this.getPreferredDevice();
if (hwDevice === null) { if (hwDevice === null) {
hwDevice = `/dev/dri/${this.devices[0]}`; hwDevice = `/dev/dri/${this.deviceSummary.driDevices[0]}`;
} }
return hwDevice; return hwDevice;
@ -631,18 +650,18 @@ export class QSVConfig extends BaseHWConfig {
} }
export class VAAPIConfig extends BaseHWConfig { export class VAAPIConfig extends BaseHWConfig {
getAccel() { getPreferredAccel() {
return 'vaapi'; return 'vaapi';
} }
getDeviceSpecifier() { getDeviceSpecifier() {
if (this.devices.length === 0) { if (this.deviceSummary.driDevices.length === 0) {
throw new Error('No VAAPI device found'); throw new Error('No VAAPI device found');
} }
let hwDevice = this.getPreferredDevice(); let hwDevice = this.getPreferredDevice();
if (hwDevice === null) { if (hwDevice === null) {
hwDevice = `/dev/dri/${this.devices[0]}`; hwDevice = `/dev/dri/${this.deviceSummary.driDevices[0]}`;
} }
return hwDevice; return hwDevice;
@ -697,14 +716,14 @@ export class RKMPPConfig extends BaseHWConfig {
} }
getDeviceOptions(): string[] { getDeviceOptions(): string[] {
if (this.devices.length === 0) { if (this.deviceSummary.driDevices.length === 0) {
throw new Error('No RKMPP device found'); throw new Error('No RKMPP device found');
} }
return [...super.getDeviceOptions(), '-afbc rga']; return [...super.getDeviceOptions(), '-afbc rga'];
} }
getAccel() { getPreferredAccel() {
return 'rkmpp'; return 'rkmpp';
} }
@ -741,8 +760,4 @@ export class RKMPPConfig extends BaseHWConfig {
getSupportedCodecs() { getSupportedCodecs() {
return [VideoCodec.H264, VideoCodec.HEVC]; return [VideoCodec.H264, VideoCodec.HEVC];
} }
getVideoCodec(): string {
return `${this.config.targetVideoCodec}_rkmpp`;
}
} }