mirror of
https://github.com/immich-app/immich.git
synced 2025-05-24 01:12:58 -04:00
fix(server): select main stream according to bitrate (#18375)
* fix main stream * update unit tests --------- Co-authored-by: mertalev <101130780+mertalev@users.noreply.github.com>
This commit is contained in:
parent
00a77c2d6a
commit
dc8962f2bc
@ -209,7 +209,7 @@ export class MediaRepository {
|
|||||||
index: stream.index,
|
index: stream.index,
|
||||||
codecType: stream.codec_type,
|
codecType: stream.codec_type,
|
||||||
codecName: stream.codec_name,
|
codecName: stream.codec_name,
|
||||||
frameCount: this.parseInt(options?.countFrames ? stream.nb_read_packets : stream.nb_frames),
|
bitrate: this.parseInt(stream.bit_rate),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -1235,7 +1235,7 @@ describe(MediaService.name, () => {
|
|||||||
expect(mocks.media.transcode).not.toHaveBeenCalled();
|
expect(mocks.media.transcode).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should transcode the longest stream', async () => {
|
it('should transcode the highest bitrate video stream', async () => {
|
||||||
mocks.logger.isLevelEnabled.mockReturnValue(false);
|
mocks.logger.isLevelEnabled.mockReturnValue(false);
|
||||||
mocks.media.probe.mockResolvedValue(probeStub.multipleVideoStreams);
|
mocks.media.probe.mockResolvedValue(probeStub.multipleVideoStreams);
|
||||||
|
|
||||||
@ -1249,7 +1249,27 @@ describe(MediaService.name, () => {
|
|||||||
'upload/encoded-video/user-id/as/se/asset-id.mp4',
|
'upload/encoded-video/user-id/as/se/asset-id.mp4',
|
||||||
expect.objectContaining({
|
expect.objectContaining({
|
||||||
inputOptions: expect.any(Array),
|
inputOptions: expect.any(Array),
|
||||||
outputOptions: expect.arrayContaining(['-map 0:0', '-map 0:1']),
|
outputOptions: expect.arrayContaining(['-map 0:1', '-map 0:3']),
|
||||||
|
twoPass: false,
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should transcode the highest bitrate audio stream', async () => {
|
||||||
|
mocks.logger.isLevelEnabled.mockReturnValue(false);
|
||||||
|
mocks.media.probe.mockResolvedValue(probeStub.multipleAudioStreams);
|
||||||
|
|
||||||
|
await sut.handleVideoConversion({ id: assetStub.video.id });
|
||||||
|
|
||||||
|
expect(mocks.media.probe).toHaveBeenCalledWith('/original/path.ext', { countFrames: false });
|
||||||
|
expect(mocks.systemMetadata.get).toHaveBeenCalled();
|
||||||
|
expect(mocks.storage.mkdirSync).toHaveBeenCalled();
|
||||||
|
expect(mocks.media.transcode).toHaveBeenCalledWith(
|
||||||
|
'/original/path.ext',
|
||||||
|
'upload/encoded-video/user-id/as/se/asset-id.mp4',
|
||||||
|
expect.objectContaining({
|
||||||
|
inputOptions: expect.any(Array),
|
||||||
|
outputOptions: expect.arrayContaining(['-map 0:0', '-map 0:2']),
|
||||||
twoPass: false,
|
twoPass: false,
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
@ -1780,7 +1800,7 @@ describe(MediaService.name, () => {
|
|||||||
'-movflags faststart',
|
'-movflags faststart',
|
||||||
'-fps_mode passthrough',
|
'-fps_mode passthrough',
|
||||||
'-map 0:0',
|
'-map 0:0',
|
||||||
'-map 0:1',
|
'-map 0:3',
|
||||||
'-v verbose',
|
'-v verbose',
|
||||||
'-vf scale=-2:720',
|
'-vf scale=-2:720',
|
||||||
'-preset 12',
|
'-preset 12',
|
||||||
@ -1901,7 +1921,7 @@ describe(MediaService.name, () => {
|
|||||||
'-movflags faststart',
|
'-movflags faststart',
|
||||||
'-fps_mode passthrough',
|
'-fps_mode passthrough',
|
||||||
'-map 0:0',
|
'-map 0:0',
|
||||||
'-map 0:1',
|
'-map 0:3',
|
||||||
'-g 256',
|
'-g 256',
|
||||||
'-v verbose',
|
'-v verbose',
|
||||||
'-vf hwupload_cuda,scale_cuda=-2:720:format=nv12',
|
'-vf hwupload_cuda,scale_cuda=-2:720:format=nv12',
|
||||||
@ -2072,7 +2092,7 @@ describe(MediaService.name, () => {
|
|||||||
'-movflags faststart',
|
'-movflags faststart',
|
||||||
'-fps_mode passthrough',
|
'-fps_mode passthrough',
|
||||||
'-map 0:0',
|
'-map 0:0',
|
||||||
'-map 0:1',
|
'-map 0:3',
|
||||||
'-bf 7',
|
'-bf 7',
|
||||||
'-refs 5',
|
'-refs 5',
|
||||||
'-g 256',
|
'-g 256',
|
||||||
@ -2294,7 +2314,7 @@ describe(MediaService.name, () => {
|
|||||||
'-movflags faststart',
|
'-movflags faststart',
|
||||||
'-fps_mode passthrough',
|
'-fps_mode passthrough',
|
||||||
'-map 0:0',
|
'-map 0:0',
|
||||||
'-map 0:1',
|
'-map 0:3',
|
||||||
'-g 256',
|
'-g 256',
|
||||||
'-v verbose',
|
'-v verbose',
|
||||||
'-vf hwupload=extra_hw_frames=64,scale_vaapi=-2:720:mode=hq:out_range=pc:format=nv12',
|
'-vf hwupload=extra_hw_frames=64,scale_vaapi=-2:720:mode=hq:out_range=pc:format=nv12',
|
||||||
@ -2581,7 +2601,7 @@ describe(MediaService.name, () => {
|
|||||||
'-movflags faststart',
|
'-movflags faststart',
|
||||||
'-fps_mode passthrough',
|
'-fps_mode passthrough',
|
||||||
'-map 0:0',
|
'-map 0:0',
|
||||||
'-map 0:1',
|
'-map 0:3',
|
||||||
'-g 256',
|
'-g 256',
|
||||||
'-v verbose',
|
'-v verbose',
|
||||||
'-vf scale_rkrga=-2:720:format=nv12:afbc=1:async_depth=4',
|
'-vf scale_rkrga=-2:720:format=nv12:afbc=1:async_depth=4',
|
||||||
|
@ -547,7 +547,7 @@ export class MediaService extends BaseService {
|
|||||||
private getMainStream<T extends VideoStreamInfo | AudioStreamInfo>(streams: T[]): T {
|
private getMainStream<T extends VideoStreamInfo | AudioStreamInfo>(streams: T[]): T {
|
||||||
return streams
|
return streams
|
||||||
.filter((stream) => stream.codecName !== 'unknown')
|
.filter((stream) => stream.codecName !== 'unknown')
|
||||||
.sort((stream1, stream2) => stream2.frameCount - stream1.frameCount)[0];
|
.sort((stream1, stream2) => stream2.bitrate - stream1.bitrate)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
private getTranscodeTarget(
|
private getTranscodeTarget(
|
||||||
|
@ -89,7 +89,7 @@ export interface VideoStreamInfo {
|
|||||||
export interface AudioStreamInfo {
|
export interface AudioStreamInfo {
|
||||||
index: number;
|
index: number;
|
||||||
codecName?: string;
|
codecName?: string;
|
||||||
frameCount: number;
|
bitrate: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface VideoFormat {
|
export interface VideoFormat {
|
||||||
|
37
server/test/fixtures/media.stub.ts
vendored
37
server/test/fixtures/media.stub.ts
vendored
@ -21,7 +21,7 @@ const probeStubDefaultVideoStream: VideoStreamInfo[] = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const probeStubDefaultAudioStream: AudioStreamInfo[] = [{ index: 1, codecName: 'mp3', frameCount: 100 }];
|
const probeStubDefaultAudioStream: AudioStreamInfo[] = [{ index: 3, codecName: 'mp3', bitrate: 100 }];
|
||||||
|
|
||||||
const probeStubDefault: VideoInfo = {
|
const probeStubDefault: VideoInfo = {
|
||||||
format: probeStubDefaultFormat,
|
format: probeStubDefaultFormat,
|
||||||
@ -40,23 +40,42 @@ export const probeStub = {
|
|||||||
height: 1080,
|
height: 1080,
|
||||||
width: 400,
|
width: 400,
|
||||||
codecName: 'hevc',
|
codecName: 'hevc',
|
||||||
frameCount: 100,
|
frameCount: 1,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
isHDR: false,
|
isHDR: false,
|
||||||
bitrate: 0,
|
bitrate: 100,
|
||||||
pixelFormat: 'yuv420p',
|
pixelFormat: 'yuv420p',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
index: 1,
|
index: 1,
|
||||||
height: 1080,
|
height: 1080,
|
||||||
width: 400,
|
width: 400,
|
||||||
codecName: 'h7000',
|
codecName: 'hevc',
|
||||||
frameCount: 99,
|
frameCount: 2,
|
||||||
rotation: 0,
|
rotation: 0,
|
||||||
isHDR: false,
|
isHDR: false,
|
||||||
bitrate: 0,
|
bitrate: 101,
|
||||||
pixelFormat: 'yuv420p',
|
pixelFormat: 'yuv420p',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
index: 2,
|
||||||
|
height: 1080,
|
||||||
|
width: 400,
|
||||||
|
codecName: 'h7000',
|
||||||
|
frameCount: 3,
|
||||||
|
rotation: 0,
|
||||||
|
isHDR: false,
|
||||||
|
bitrate: 99,
|
||||||
|
pixelFormat: 'yuv420p',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
}),
|
||||||
|
multipleAudioStreams: Object.freeze<VideoInfo>({
|
||||||
|
...probeStubDefault,
|
||||||
|
audioStreams: [
|
||||||
|
{ index: 0, codecName: 'mp3', bitrate: 100 },
|
||||||
|
{ index: 1, codecName: 'mp3', bitrate: 101 },
|
||||||
|
{ index: 2, codecName: 'mp3', bitrate: 102 },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
noHeight: Object.freeze<VideoInfo>({
|
noHeight: Object.freeze<VideoInfo>({
|
||||||
@ -200,13 +219,13 @@ export const probeStub = {
|
|||||||
}),
|
}),
|
||||||
audioStreamAac: Object.freeze<VideoInfo>({
|
audioStreamAac: Object.freeze<VideoInfo>({
|
||||||
...probeStubDefault,
|
...probeStubDefault,
|
||||||
audioStreams: [{ index: 1, codecName: 'aac', frameCount: 100 }],
|
audioStreams: [{ index: 1, codecName: 'aac', bitrate: 100 }],
|
||||||
}),
|
}),
|
||||||
audioStreamUnknown: Object.freeze<VideoInfo>({
|
audioStreamUnknown: Object.freeze<VideoInfo>({
|
||||||
...probeStubDefault,
|
...probeStubDefault,
|
||||||
audioStreams: [
|
audioStreams: [
|
||||||
{ index: 0, codecName: 'aac', frameCount: 100 },
|
{ index: 0, codecName: 'aac', bitrate: 100 },
|
||||||
{ index: 1, codecName: 'unknown', frameCount: 200 },
|
{ index: 1, codecName: 'unknown', bitrate: 200 },
|
||||||
],
|
],
|
||||||
}),
|
}),
|
||||||
matroskaContainer: Object.freeze<VideoInfo>({
|
matroskaContainer: Object.freeze<VideoInfo>({
|
||||||
|
Loading…
x
Reference in New Issue
Block a user