diff --git a/Sources/StreamRecorderKit/FFmpegRecorder.swift b/Sources/StreamRecorderKit/FFmpegRecorder.swift index 63528ac..58c7c6f 100644 --- a/Sources/StreamRecorderKit/FFmpegRecorder.swift +++ b/Sources/StreamRecorderKit/FFmpegRecorder.swift @@ -37,6 +37,7 @@ public final class FFmpegRecorder: StreamRecorderEngine { private var process: Process? private var duration: Double = 0 private var stopRequested = false + private var stderrAccumulator = "" public init() {} @@ -44,6 +45,7 @@ public final class FFmpegRecorder: StreamRecorderEngine { guard !isRecording else { return } self.duration = duration + self.stderrAccumulator = "" // 查找 ffmpeg let ffmpegPath = findFFmpeg() @@ -53,6 +55,7 @@ public final class FFmpegRecorder: StreamRecorderEngine { // 解析 HLS 变体 let resolvedURL = HLSVariantResolver.resolve(url: url, maxWidth: 1280) ?? url + NSLog("[Recorder] ffmpeg input URL: %@", resolvedURL.absoluteString) // 删除已存在的输出文件 if FileManager.default.fileExists(atPath: outputPath) { @@ -78,12 +81,13 @@ public final class FFmpegRecorder: StreamRecorderEngine { outputPath ] proc.arguments = args + NSLog("[Recorder] ffmpeg args: %@", args.joined(separator: " ")) let stderrPipe = Pipe() proc.standardError = stderrPipe proc.standardOutput = FileHandle.nullDevice - // 解析进度 + // 解析进度 + 收集 stderr let progressState = ProgressState() stderrPipe.fileHandleForReading.readabilityHandler = { [weak self] handle in guard let self = self else { return } @@ -91,6 +95,7 @@ public final class FFmpegRecorder: StreamRecorderEngine { guard !data.isEmpty else { return } let str = String(data: data, encoding: .utf8) ?? "" + self.stderrAccumulator += str if let timeRange = str.range(of: "time=") { let sub = String(str[timeRange.upperBound...]) if let endRange = sub.range(of: " ") ?? sub.range(of: "\n") ?? sub.range(of: "\r") { @@ -128,7 +133,9 @@ public final class FFmpegRecorder: StreamRecorderEngine { isRecording = false guard proc.terminationStatus == 0 || stopRequested else { - throw RecordingError.processFailed(Int(proc.terminationStatus)) + let lastStderr = String(stderrAccumulator.suffix(2000)) + NSLog("[Recorder] ffmpeg stderr (last 2000 chars):\n%@", lastStderr) + throw RecordingError.processFailed(Int(proc.terminationStatus), lastStderr) } guard FileManager.default.fileExists(atPath: outputPath) else { diff --git a/Sources/StreamRecorderKit/StreamRecorderEngine.swift b/Sources/StreamRecorderKit/StreamRecorderEngine.swift index 45075fb..d921ffa 100644 --- a/Sources/StreamRecorderKit/StreamRecorderEngine.swift +++ b/Sources/StreamRecorderKit/StreamRecorderEngine.swift @@ -57,7 +57,7 @@ public enum RecordingError: Error, LocalizedError { case invalidURL case noFFmpeg case noMediaTracks - case processFailed(Int) + case processFailed(Int, String? = nil) case outputNotFound case unknown(String) @@ -66,7 +66,12 @@ public enum RecordingError: Error, LocalizedError { case .invalidURL: return "无效的 URL" case .noFFmpeg: return "未找到 ffmpeg,请安装: brew install ffmpeg" case .noMediaTracks: return "未找到任何媒体轨道" - case .processFailed(let code): return "录制失败 (exit code: \(code))" + case .processFailed(let code, let stderr): + if let s = stderr, !s.isEmpty { + let preview = String(s.prefix(300)) + return "录制失败 (exit code: \(code)):\n\(preview)" + } + return "录制失败 (exit code: \(code))" case .outputNotFound: return "输出文件不存在" case .unknown(let msg): return msg }