diff --git a/Sources/StreamRecorderKit/FFmpegRecorder.swift b/Sources/StreamRecorderKit/FFmpegRecorder.swift index 2a10ce1..7901a49 100644 --- a/Sources/StreamRecorderKit/FFmpegRecorder.swift +++ b/Sources/StreamRecorderKit/FFmpegRecorder.swift @@ -106,8 +106,8 @@ public final class FFmpegRecorder: StreamRecorderEngine { throw RecordingError.unknown("无法启动 ffmpeg: \(error.localizedDescription)") } - // 等待完成 - proc.waitUntilExit() + // 异步等待进程完成(不阻塞 cooperative thread) + await waitForProcess(proc) stderrPipe.fileHandleForReading.readabilityHandler = nil isRecording = false @@ -136,6 +136,22 @@ public final class FFmpegRecorder: StreamRecorderEngine { // MARK: - Private + /// Async-friendly process wait — uses terminationHandler + continuation + /// instead of blocking waitUntilExit() which deadlocks in Task context + private func waitForProcess(_ proc: Process) async { + guard proc.isRunning else { return } + await withCheckedContinuation { (continuation: CheckedContinuation) in + proc.terminationHandler = { _ in + continuation.resume() + } + // Race condition: process may have exited between isRunning check and setting handler + if !proc.isRunning { + proc.terminationHandler = nil + continuation.resume() + } + } + } + private func findFFmpeg() -> String { let paths = [ "/opt/homebrew/bin/ffmpeg", @@ -186,7 +202,7 @@ public final class FFmpegRecorder: StreamRecorderEngine { do { try proc.run() - proc.waitUntilExit() + await waitForProcess(proc) } catch { throw RecordingError.unknown("无法分析输出文件") } @@ -269,7 +285,7 @@ public final class FFmpegRecorder: StreamRecorderEngine { do { try proc.run() - proc.waitUntilExit() + await waitForProcess(proc) } catch { return nil }