JJ_MKVMaker/test/video_adjust_test.dart

78 lines
4.3 KiB
Dart

import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:jj_mkvmaker/app/settings.dart';
import 'package:jj_mkvmaker/core/encode_options.dart';
import 'package:jj_mkvmaker/core/models.dart';
import 'package:path/path.dart' as p;
const _land = StreamInfo(index: 0, type: 'video', codec: 'h264', width: 1920, height: 1080);
void main() {
test('필터: 바꾼 것이 없으면 없음, 크기만 바꾸면 예전과 같음', () {
expect(buildVideoFilter(const EncodeSettings(codec: VideoCodecChoice.h264), _land), isNull);
expect(buildVideoFilter(const EncodeSettings(codec: VideoCodecChoice.h264, resolution: ResolutionChoice.p720), _land),
'scale=-2:720:flags=lanczos');
});
test('필터: 세로 9:16 채우기 · 맞추기 · 흐린 배경, 회전, 색 보정', () {
const s = EncodeSettings(codec: VideoCodecChoice.h264, frame: FrameChoice.portrait);
expect(buildVideoFilter(s, _land),
'scale=1080:1920:force_original_aspect_ratio=increase:flags=lanczos,crop=1080:1920,setsar=1');
expect(buildVideoFilter(s.copyWith(fit: FitChoice.fit), _land), contains('pad=1080:1920'));
expect(buildVideoFilter(s.copyWith(fit: FitChoice.blur), _land), contains('boxblur'));
// 720p 를 고르면 짧은 변 720
expect(buildVideoFilter(s.copyWith(resolution: ResolutionChoice.p720), _land), contains('crop=720:1280'));
expect(buildVideoFilter(const EncodeSettings(rotate: RotateChoice.cw90), _land), 'transpose=1');
expect(buildVideoFilter(const EncodeSettings(saturation: 20, temperature: -40), _land),
'eq=brightness=0.000:contrast=1.000:saturation=1.200,colorbalance=rs=-0.100:rm=-0.100:rh=-0.100:bs=0.100:bm=0.100:bh=0.100');
expect(const EncodeSettings(frame: FrameChoice.portrait, saturation: 20).adjustSummary, contains('채도 +20'));
});
test('설정 저장: 화면 비율 · 맞추기 · 회전 · 색 보정', () {
final s = AppSettings()
..mkv.encode = const EncodeSettings(
codec: VideoCodecChoice.h264,
frame: FrameChoice.square,
fit: FitChoice.blur,
rotate: RotateChoice.ccw90,
brightness: 10,
contrast: -5,
saturation: 30,
temperature: 100);
final e = AppSettings.fromJson(s.toJson()).mkv.encode;
expect([e.frame, e.fit, e.rotate], [FrameChoice.square, FitChoice.blur, RotateChoice.ccw90]);
expect([e.brightness, e.contrast, e.saturation, e.temperature], [10, -5, 30, 100]);
expect(AppSettings.fromJson({'encode': {'saturation': 999}}).mkv.encode.saturation, 100);
});
// 실제 FFmpeg 로 만들어 보고 출력 크기 확인
final ffmpeg = p.join(Directory.current.path, 'third_party', 'ffmpeg', 'windows', 'ffmpeg.exe');
test('실제 FFmpeg: 가로 영상 → 세로 9:16 (세 가지 맞추기) · 회전 · 색 보정이 오류 없이 되고 크기가 맞다', () async {
final dir = Directory.systemTemp.createTempSync('jj_adj_');
addTearDown(() => dir.deleteSync(recursive: true));
const src = StreamInfo(index: 0, type: 'video', codec: 'h264', width: 640, height: 360);
final cases = {
'fill': (const EncodeSettings(codec: VideoCodecChoice.h264, frame: FrameChoice.portrait), '360x640'),
'fit': (const EncodeSettings(codec: VideoCodecChoice.h264, frame: FrameChoice.portrait, fit: FitChoice.fit), '360x640'),
'blur': (
const EncodeSettings(
codec: VideoCodecChoice.h264, frame: FrameChoice.portrait, fit: FitChoice.blur, saturation: 30, temperature: 50),
'360x640'
),
'rotate': (const EncodeSettings(codec: VideoCodecChoice.h264, rotate: RotateChoice.cw90, brightness: 20), '360x640'),
'square': (const EncodeSettings(codec: VideoCodecChoice.h264, frame: FrameChoice.square, contrast: 40), '360x360'),
};
for (final MapEntry(key: name, value: (s, size)) in cases.entries) {
final out = p.join(dir.path, '$name.png');
final r = await Process.run(ffmpeg, [
'-hide_banner', '-v', 'error', '-f', 'lavfi', '-i', 'testsrc2=size=640x360:duration=1', //
'-frames:v', '1', '-vf', buildVideoFilter(s, src)!, '-y', out,
]);
expect(r.exitCode, 0, reason: '$name: ${r.stderr}');
final probe = await Process.run(ffmpeg, ['-hide_banner', '-i', out]);
expect('${probe.stderr}', contains(size), reason: name);
}
}, skip: File(ffmpeg).existsSync() ? false : 'ffmpeg 없음');
}