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:
parent
b98d1bf9d3
commit
9a2fa21b28
3 changed files with 106 additions and 4 deletions
|
@ -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 }]);
|
||||
|
|
|
@ -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 };
|
||||
}
|
||||
|
||||
|
|
30
server/test/fixtures/media.stub.ts
vendored
30
server/test/fixtures/media.stub.ts
vendored
|
@ -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 }],
|
||||
|
|
Loading…
Reference in a new issue