v2026.10.02_001: Android MKV 만들기가 끝나도 몇 분씩 "작업 중" 으로 멈추던 문제 수정
- ffmpeg-kit 플러그인 사본: 통계(진행률) 이벤트를 세션마다 0.25초에 한 번만 Dart 로 보냄 (프레임마다 보내 메인 스레드 줄이 밀리고 완료 이벤트가 늦게 도착, 그동안 다른 작업 버튼이 모두 잠김) - FfmpegKitMediaTool: 진행률이 0.5% 이상 바뀔 때만 화면 갱신 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>main
parent
bf6429dc76
commit
467c0ea8b5
|
|
@ -71,6 +71,7 @@ class FfmpegKitMediaTool implements MediaTool {
|
||||||
final totalMs = duration?.inMilliseconds ?? 0;
|
final totalMs = duration?.inMilliseconds ?? 0;
|
||||||
final done = Completer<void>();
|
final done = Completer<void>();
|
||||||
final errTail = <String>[];
|
final errTail = <String>[];
|
||||||
|
var lastProgress = 0.0;
|
||||||
final session = await FFmpegKit.executeWithArgumentsAsync(
|
final session = await FFmpegKit.executeWithArgumentsAsync(
|
||||||
a,
|
a,
|
||||||
(s) async {
|
(s) async {
|
||||||
|
|
@ -93,9 +94,12 @@ class FfmpegKitMediaTool implements MediaTool {
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
(st) {
|
(st) {
|
||||||
if (onProgress != null && totalMs > 0 && st.getTime() > 0) {
|
if (onProgress == null || totalMs <= 0 || st.getTime() <= 0) return;
|
||||||
onProgress((st.getTime() / totalMs).clamp(0.0, 1.0));
|
// 화면을 다시 그리는 일이 많지 않도록 0.5% 이상 바뀔 때만 알린다
|
||||||
}
|
final x = (st.getTime() / totalMs).clamp(0.0, 1.0);
|
||||||
|
if (x - lastProgress < 0.005) return;
|
||||||
|
lastProgress = x;
|
||||||
|
onProgress(x);
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
final id = session.getSessionId();
|
final id = session.getSessionId();
|
||||||
|
|
|
||||||
|
|
@ -9,3 +9,8 @@ JJ_MKVMaker 의 Windows 판은 동봉한 ffmpeg.exe 를 쓰므로 필요 없다.
|
||||||
## 수정
|
## 수정
|
||||||
- pubspec.yaml: 플러그인 플랫폼을 android 만 남김 (ios · macos · windows · linux 제거)
|
- pubspec.yaml: 플러그인 플랫폼을 android 만 남김 (ios · macos · windows · linux 제거)
|
||||||
- windows · linux · macos · ios · example 폴더 제거
|
- windows · linux · macos · ios · example 폴더 제거
|
||||||
|
|
||||||
|
## 통계 이벤트 줄이기 (2026-10-02)
|
||||||
|
FFmpeg 는 프레임마다 통계(진행률)를 보낸다. 17분 영상 하나에 수만 개가 Dart 로 넘어가 메인 스레드 줄이 밀리고,
|
||||||
|
완료 이벤트가 그 뒤에 서 있어 FFmpeg 가 4초 만에 끝나도 앱은 2분 넘게 "작업 중" 으로 남았다.
|
||||||
|
- android/.../FFmpegKitFlutterPlugin.java: 통계는 세션마다 0.25초에 한 번만 보낸다 (`shouldEmitStatistics`)
|
||||||
|
|
|
||||||
|
|
@ -161,6 +161,11 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met
|
||||||
});
|
});
|
||||||
|
|
||||||
FFmpegKitConfig.enableStatisticsCallback(statistics -> {
|
FFmpegKitConfig.enableStatisticsCallback(statistics -> {
|
||||||
|
// JJ_PATCH: FFmpeg 는 프레임마다 통계를 보낸다 (긴 영상이면 수만 개).
|
||||||
|
// 모두 Dart 로 보내면 메인 스레드 줄이 밀려 완료 이벤트가 몇 분씩 늦게 도착한다 → 세션마다 0.25초에 한 번만
|
||||||
|
if (!shouldEmitStatistics(statistics)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
for (FFmpegKitFlutterPlugin plugin : attachedPlugins) {
|
for (FFmpegKitFlutterPlugin plugin : attachedPlugins) {
|
||||||
if (plugin.statisticsEnabled.get() && plugin.eventSink != null) {
|
if (plugin.statisticsEnabled.get() && plugin.eventSink != null) {
|
||||||
plugin.emitStatistics(statistics);
|
plugin.emitStatistics(statistics);
|
||||||
|
|
@ -169,7 +174,23 @@ public class FFmpegKitFlutterPlugin implements FlutterPlugin, ActivityAware, Met
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// JJ_PATCH: 세션별 마지막 통계 전송 시각 (완료되면 지운다)
|
||||||
|
private static final long STATISTICS_INTERVAL_MS = 250;
|
||||||
|
private static final java.util.concurrent.ConcurrentHashMap<Long, Long> lastStatisticsAt =
|
||||||
|
new java.util.concurrent.ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
private static boolean shouldEmitStatistics(final Statistics statistics) {
|
||||||
|
final long now = android.os.SystemClock.uptimeMillis();
|
||||||
|
final Long last = lastStatisticsAt.get(statistics.getSessionId());
|
||||||
|
if (last != null && now - last < STATISTICS_INTERVAL_MS) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
lastStatisticsAt.put(statistics.getSessionId(), now);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
private static void broadcastSession(final Session session) {
|
private static void broadcastSession(final Session session) {
|
||||||
|
lastStatisticsAt.remove(session.getSessionId());
|
||||||
boolean delivered = false;
|
boolean delivered = false;
|
||||||
for (FFmpegKitFlutterPlugin plugin : attachedPlugins) {
|
for (FFmpegKitFlutterPlugin plugin : attachedPlugins) {
|
||||||
if (plugin.eventSink != null) {
|
if (plugin.eventSink != null) {
|
||||||
|
|
|
||||||
|
|
@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev
|
||||||
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html
|
||||||
# In Windows, build-name is used as the major, minor, and patch parts
|
# In Windows, build-name is used as the major, minor, and patch parts
|
||||||
# of the product and file versions while build-number is used as the build suffix.
|
# of the product and file versions while build-number is used as the build suffix.
|
||||||
version: 2026.10.1+3
|
version: 2026.10.2+1
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: ^3.13.4
|
sdk: ^3.13.4
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue