1
0
Fork 0
mirror of https://github.com/immich-app/immich.git synced 2024-12-29 15:11:58 +00:00

fix(server): scale transcoded videos if dimensions are odd (#6461)

scale if odd resolution
This commit is contained in:
Mert 2024-01-17 22:16:44 -05:00 committed by GitHub
parent b98d1bf9d3
commit 9a2fa21b28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 106 additions and 4 deletions

View file

@ -600,6 +600,66 @@ describe(MediaService.name, () => {
);
});
it('should always scale video if height is uneven', async () => {
mediaMock.probe.mockResolvedValue(probeStub.videoStreamOddHeight);
configMock.load.mockResolvedValue([
{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.ALL },
{ key: SystemConfigKey.FFMPEG_TARGET_RESOLUTION, value: 'original' },
]);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleVideoConversion({ id: assetStub.video.id });
expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext',
'upload/encoded-video/user-id/as/se/asset-id.mp4',
{
inputOptions: [],
outputOptions: [
'-c:v h264',
'-c:a aac',
'-movflags faststart',
'-fps_mode passthrough',
'-map 0:0',
'-map 0:1',
'-v verbose',
`-vf scale=-2:354,format=yuv420p`,
'-preset ultrafast',
'-crf 23',
],
twoPass: false,
},
);
});
it('should always scale video if width is uneven', async () => {
mediaMock.probe.mockResolvedValue(probeStub.videoStreamOddWidth);
configMock.load.mockResolvedValue([
{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.ALL },
{ key: SystemConfigKey.FFMPEG_TARGET_RESOLUTION, value: 'original' },
]);
assetMock.getByIds.mockResolvedValue([assetStub.video]);
await sut.handleVideoConversion({ id: assetStub.video.id });
expect(mediaMock.transcode).toHaveBeenCalledWith(
'/original/path.ext',
'upload/encoded-video/user-id/as/se/asset-id.mp4',
{
inputOptions: [],
outputOptions: [
'-c:v h264',
'-c:a aac',
'-movflags faststart',
'-fps_mode passthrough',
'-map 0:0',
'-map 0:1',
'-v verbose',
`-vf scale=354:-2,format=yuv420p`,
'-preset ultrafast',
'-crf 23',
],
twoPass: false,
},
);
});
it('should transcode when audio doesnt match target', async () => {
mediaMock.probe.mockResolvedValue(probeStub.audioStreamMp3);
configMock.load.mockResolvedValue([{ key: SystemConfigKey.FFMPEG_TRANSCODE, value: TranscodePolicy.OPTIMAL }]);

View file

@ -122,15 +122,24 @@ class BaseConfig implements VideoCodecSWConfig {
}
getTargetResolution(videoStream: VideoStreamInfo) {
let target;
if (this.config.targetResolution === 'original') {
return Math.min(videoStream.height, videoStream.width);
target = Math.min(videoStream.height, videoStream.width);
} else {
target = Number.parseInt(this.config.targetResolution);
}
return Number.parseInt(this.config.targetResolution);
if (target % 2 !== 0) {
target -= 1;
}
return target;
}
shouldScale(videoStream: VideoStreamInfo) {
return Math.min(videoStream.height, videoStream.width) > this.getTargetResolution(videoStream);
const oddDimensions = videoStream.height % 2 !== 0 || videoStream.width % 2 !== 0;
const largerThanTarget = Math.min(videoStream.height, videoStream.width) > this.getTargetResolution(videoStream);
return oddDimensions || largerThanTarget;
}
shouldToneMap(videoStream: VideoStreamInfo) {
@ -146,7 +155,10 @@ class BaseConfig implements VideoCodecSWConfig {
getSize(videoStream: VideoStreamInfo) {
const smaller = this.getTargetResolution(videoStream);
const factor = Math.max(videoStream.height, videoStream.width) / Math.min(videoStream.height, videoStream.width);
const larger = Math.round(smaller * factor);
let larger = Math.round(smaller * factor);
if (larger % 2 !== 0) {
larger -= 1;
}
return this.isVideoVertical(videoStream) ? { width: smaller, height: larger } : { width: larger, height: smaller };
}

View file

@ -117,6 +117,36 @@ export const probeStub = {
},
],
}),
videoStreamOddHeight: Object.freeze<VideoInfo>({
...probeStubDefault,
videoStreams: [
{
index: 0,
height: 355,
width: 1586,
codecName: 'h264',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
},
],
}),
videoStreamOddWidth: Object.freeze<VideoInfo>({
...probeStubDefault,
videoStreams: [
{
index: 0,
height: 1586,
width: 355,
codecName: 'h264',
codecType: 'video',
frameCount: 100,
rotation: 0,
isHDR: false,
},
],
}),
audioStreamMp3: Object.freeze<VideoInfo>({
...probeStubDefault,
audioStreams: [{ index: 1, codecType: 'audio', codecName: 'aac', frameCount: 100 }],