mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
feat(server): easy RKMPP video encoding (#7460)
* feat(server): easy RKMPP video encoding * make linter happy
This commit is contained in:
parent
cfb49c8be0
commit
5e485e35e9
@ -38,12 +38,6 @@ services:
|
|||||||
- /dev/dri:/dev/dri
|
- /dev/dri:/dev/dri
|
||||||
- /dev/dma_heap:/dev/dma_heap
|
- /dev/dma_heap:/dev/dma_heap
|
||||||
- /dev/mpp_service:/dev/mpp_service
|
- /dev/mpp_service:/dev/mpp_service
|
||||||
volumes:
|
|
||||||
- /usr/bin/ffmpeg:/usr/bin/ffmpeg_mpp:ro
|
|
||||||
- /lib/aarch64-linux-gnu:/lib/ffmpeg-mpp:ro
|
|
||||||
- /lib/aarch64-linux-gnu/libblas.so.3:/lib/ffmpeg-mpp/libblas.so.3:ro # symlink is resolved by mounting
|
|
||||||
- /lib/aarch64-linux-gnu/liblapack.so.3:/lib/ffmpeg-mpp/liblapack.so.3:ro # symlink is resolved by mounting
|
|
||||||
- /lib/aarch64-linux-gnu/pulseaudio/libpulsecommon-15.99.so:/lib/ffmpeg-mpp/libpulsecommon-15.99.so:ro
|
|
||||||
|
|
||||||
vaapi:
|
vaapi:
|
||||||
devices:
|
devices:
|
||||||
|
@ -1801,7 +1801,7 @@ describe(MediaService.name, () => {
|
|||||||
{
|
{
|
||||||
inputOptions: [],
|
inputOptions: [],
|
||||||
outputOptions: [
|
outputOptions: [
|
||||||
`-c:v hevc_rkmpp_encoder`,
|
`-c:v hevc_rkmpp`,
|
||||||
'-c:a copy',
|
'-c:a copy',
|
||||||
'-movflags faststart',
|
'-movflags faststart',
|
||||||
'-fps_mode passthrough',
|
'-fps_mode passthrough',
|
||||||
@ -1810,17 +1810,12 @@ describe(MediaService.name, () => {
|
|||||||
'-g 256',
|
'-g 256',
|
||||||
'-tag:v hvc1',
|
'-tag:v hvc1',
|
||||||
'-v verbose',
|
'-v verbose',
|
||||||
|
'-vf scale=-2:720,format=yuv420p',
|
||||||
'-level 153',
|
'-level 153',
|
||||||
'-rc_mode 3',
|
'-rc_mode 3',
|
||||||
'-quality_min 0',
|
|
||||||
'-quality_max 100',
|
|
||||||
'-b:v 10000k',
|
'-b:v 10000k',
|
||||||
'-width 1280',
|
|
||||||
'-height 720',
|
|
||||||
],
|
],
|
||||||
twoPass: false,
|
twoPass: false,
|
||||||
ffmpegPath: 'ffmpeg_mpp',
|
|
||||||
ldLibraryPath: '/lib/aarch64-linux-gnu:/lib/ffmpeg-mpp',
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
@ -1841,7 +1836,7 @@ describe(MediaService.name, () => {
|
|||||||
{
|
{
|
||||||
inputOptions: [],
|
inputOptions: [],
|
||||||
outputOptions: [
|
outputOptions: [
|
||||||
`-c:v h264_rkmpp_encoder`,
|
`-c:v h264_rkmpp`,
|
||||||
'-c:a copy',
|
'-c:a copy',
|
||||||
'-movflags faststart',
|
'-movflags faststart',
|
||||||
'-fps_mode passthrough',
|
'-fps_mode passthrough',
|
||||||
@ -1849,16 +1844,12 @@ describe(MediaService.name, () => {
|
|||||||
'-map 0:1',
|
'-map 0:1',
|
||||||
'-g 256',
|
'-g 256',
|
||||||
'-v verbose',
|
'-v verbose',
|
||||||
|
'-vf scale=-2:720,format=yuv420p',
|
||||||
'-level 51',
|
'-level 51',
|
||||||
'-rc_mode 2',
|
'-rc_mode 2',
|
||||||
'-quality_min 51',
|
'-qp_init 30',
|
||||||
'-quality_max 51',
|
|
||||||
'-width 1280',
|
|
||||||
'-height 720',
|
|
||||||
],
|
],
|
||||||
twoPass: false,
|
twoPass: false,
|
||||||
ffmpegPath: 'ffmpeg_mpp',
|
|
||||||
ldLibraryPath: '/lib/aarch64-linux-gnu:/lib/ffmpeg-mpp',
|
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
@ -607,16 +607,6 @@ export class VAAPIConfig extends BaseHWConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export class RKMPPConfig extends BaseHWConfig {
|
export class RKMPPConfig extends BaseHWConfig {
|
||||||
getOptions(target: TranscodeTarget, videoStream: VideoStreamInfo, audioStream?: AudioStreamInfo): TranscodeOptions {
|
|
||||||
const options = super.getOptions(target, videoStream, audioStream);
|
|
||||||
options.ffmpegPath = 'ffmpeg_mpp';
|
|
||||||
options.ldLibraryPath = '/lib/aarch64-linux-gnu:/lib/ffmpeg-mpp';
|
|
||||||
if ([TranscodeTarget.ALL, TranscodeTarget.VIDEO].includes(target)) {
|
|
||||||
options.outputOptions.push(...this.getSizeOptions(videoStream));
|
|
||||||
}
|
|
||||||
return options;
|
|
||||||
}
|
|
||||||
|
|
||||||
eligibleForTwoPass(): boolean {
|
eligibleForTwoPass(): boolean {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -628,18 +618,6 @@ export class RKMPPConfig extends BaseHWConfig {
|
|||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getFilterOptions(videoStream: VideoStreamInfo) {
|
|
||||||
return this.shouldToneMap(videoStream) ? this.getToneMapping() : [];
|
|
||||||
}
|
|
||||||
|
|
||||||
getSizeOptions(videoStream: VideoStreamInfo) {
|
|
||||||
if (this.shouldScale(videoStream)) {
|
|
||||||
const { width, height } = this.getSize(videoStream);
|
|
||||||
return [`-width ${width}`, `-height ${height}`];
|
|
||||||
}
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
|
|
||||||
getPresetOptions() {
|
getPresetOptions() {
|
||||||
switch (this.config.targetVideoCodec) {
|
switch (this.config.targetVideoCodec) {
|
||||||
case VideoCodec.H264: {
|
case VideoCodec.H264: {
|
||||||
@ -659,12 +637,11 @@ export class RKMPPConfig extends BaseHWConfig {
|
|||||||
getBitrateOptions() {
|
getBitrateOptions() {
|
||||||
const bitrate = this.getMaxBitrateValue();
|
const bitrate = this.getMaxBitrateValue();
|
||||||
if (bitrate > 0) {
|
if (bitrate > 0) {
|
||||||
return ['-rc_mode 3', '-quality_min 0', '-quality_max 100', `-b:v ${bitrate}${this.getBitrateUnit()}`];
|
// -b:v specifies max bitrate, average bitrate is derived automatically...
|
||||||
} else {
|
return ['-rc_mode 3', `-b:v ${bitrate}${this.getBitrateUnit()}`];
|
||||||
// convert CQP from 51-10 to 0-100, values below 10 are set to 10
|
|
||||||
const quality = Math.floor(125 - Math.max(this.config.crf, 10) * (125 / 51));
|
|
||||||
return ['-rc_mode 2', `-quality_min ${quality}`, `-quality_max ${quality}`];
|
|
||||||
}
|
}
|
||||||
|
// use CRF value as QP value
|
||||||
|
return ['-rc_mode 2', `-qp_init ${this.config.crf}`];
|
||||||
}
|
}
|
||||||
|
|
||||||
getSupportedCodecs() {
|
getSupportedCodecs() {
|
||||||
@ -672,6 +649,6 @@ export class RKMPPConfig extends BaseHWConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getVideoCodec(): string {
|
getVideoCodec(): string {
|
||||||
return `${this.config.targetVideoCodec}_rkmpp_encoder`;
|
return `${this.config.targetVideoCodec}_rkmpp`;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,8 +51,6 @@ export interface TranscodeOptions {
|
|||||||
inputOptions: string[];
|
inputOptions: string[];
|
||||||
outputOptions: string[];
|
outputOptions: string[];
|
||||||
twoPass: boolean;
|
twoPass: boolean;
|
||||||
ffmpegPath?: string;
|
|
||||||
ldLibraryPath?: string;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface BitrateDistribution {
|
export interface BitrateDistribution {
|
||||||
|
@ -76,18 +76,7 @@ export class MediaRepository implements IMediaRepository {
|
|||||||
transcode(input: string, output: string | Writable, options: TranscodeOptions): Promise<void> {
|
transcode(input: string, output: string | Writable, options: TranscodeOptions): Promise<void> {
|
||||||
if (!options.twoPass) {
|
if (!options.twoPass) {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const oldLdLibraryPath = process.env.LD_LIBRARY_PATH;
|
this.configureFfmpegCall(input, output, options).on('error', reject).on('end', resolve).run();
|
||||||
if (options.ldLibraryPath) {
|
|
||||||
// fluent ffmpeg does not allow to set environment variables, so we do it manually
|
|
||||||
process.env.LD_LIBRARY_PATH = this.chainPath(oldLdLibraryPath || '', options.ldLibraryPath);
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
this.configureFfmpegCall(input, output, options).on('error', reject).on('end', resolve).run();
|
|
||||||
} finally {
|
|
||||||
if (options.ldLibraryPath) {
|
|
||||||
process.env.LD_LIBRARY_PATH = oldLdLibraryPath;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +110,6 @@ export class MediaRepository implements IMediaRepository {
|
|||||||
|
|
||||||
configureFfmpegCall(input: string, output: string | Writable, options: TranscodeOptions) {
|
configureFfmpegCall(input: string, output: string | Writable, options: TranscodeOptions) {
|
||||||
return ffmpeg(input, { niceness: 10 })
|
return ffmpeg(input, { niceness: 10 })
|
||||||
.setFfmpegPath(options.ffmpegPath || 'ffmpeg')
|
|
||||||
.inputOptions(options.inputOptions)
|
.inputOptions(options.inputOptions)
|
||||||
.outputOptions(options.outputOptions)
|
.outputOptions(options.outputOptions)
|
||||||
.output(output)
|
.output(output)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user