feat: capture ffmpeg stderr on failure for diagnostics
- Accumulate stderr in FFmpegRecorder during recording - Log last 2000 chars of stderr when ffmpeg exits non-zero - Log ffmpeg input URL and args at start - RecordingError.processFailed now includes optional stderr string - Error message shows ffmpeg error details to user
This commit is contained in:
parent
6f2661c3d1
commit
fa38caa5a5
@ -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 {
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user